سؤال

Story: I've 3 functions from 3 different classes. Functions calling order is:

Form1_Load(...) -> Student.GetAllStudents(...) -> StudentDAL.GetStudentInformation(...) -> ConnectionManager.GetConnection(...)

What I want to do is to display StackTrace of the inner most function i.e. ConnectionManager.GetConnection(), in a MessageBox in Form1 class. In other words I don't want to use MessageBox in any inner classes, but only in outer most class that is Form1 class.

Problem: To get inner exceptions we can use InnerException or GetBaseException() etc. but when I try to get inner exception it throws an exception "Object reference not set to an instance", meaning that there is no inner exception and, when I check, the value is also null. All I want to know here why it's null? Shouldn't it be holding reference to the inner exception? Correct me if I'm wrong.

Function codes :

  1. Form1_Load(...)

    private void Form1_Load(object sender, EventArgs e)
          {
               try
               {
                    DataTable dt = new DataTable();
                    dt.Load((**new Student().GetAllStudents()**));
                    if (dt.Rows.Count <= 0)
                    {
                         MessageBox.Show("Student table empty.");
                    }
                    else
                    {
                         this.dataGridView1.DataSource = dt;
                    }
               }
               catch (Exception ex)
               {
                    MessageBox.Show(ex.Message+Environment.NewLine+"Source(s) : "+ex.StackTrace.Substring(0, ex.StackTrace.LastIndexOf("at")));
               }
    
  2. GetAllStudents(...)

    public SqlDataReader GetAllStudents()
          {
               try
               {
                    return StudentInformationDataAccessLayer.GetStudentInformation();
               }
               catch (Exception ex)
               {
                    throw ex;
               }
          }
    
  3. GetStudentInformation(...)

    public static SqlDataReader GetStudentInformation()
          {
               try
               {
                    SqlConnection sqlCon = null;
                    sqlCon = ConnectionManager.GetConnection();
                    if (sqlCon == null)
                    {
                         return null;
                    }
                    String Query = null;
                    Query  = "SELECT * FROM [dbo].[Student]";
                    SqlCommand cmd = new SqlCommand(Query, sqlCon);
                    SqlDataReader dr = null;
                    dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
                    return dr;
               }
               catch (Exception ex)
               {
                    throw ex;
               }
          }
    
  4. GetConnection(...)

    public static SqlConnection GetConnection()
          {
               String _connectionString = null;
               _connectionString = ConfigurationManager.ConnectionStrings["Default"].ConnectionString;
    
               if (_connectionString == null)
               {
                    return null;
               }
    
               try
               {
                    SqlConnection connection = new SqlConnection(_connectionString);
                    connection.Open();
                    return connection;
               }
               catch (Exception ex)
               {
                    throw ex;
               }
          }
    
هل كانت مفيدة؟

المحلول

If you want to re-throw with inner exception set, use below code, but remember that you will lose stack trace:

try
{
   ...
}
catch (Exception ex)
{
  throw new Exception("message", ex);
}

To just re-throw an exception and preserve stack trace, use:

try
{
   ...
}
catch (Exception ex)
{
  throw;
}

نصائح أخرى

If you want stack trace and exception information to be preserved, you should change the code that re-throws caught exceptions like this:

 }
 catch(Exception ex)
 {
     // do what you need to do with ex
     // ..
     // rethrow..

     throw;           // notice this is not "throw ex";
 }

Re-throwing the exception using just throw; preserves the original stack trace. There won't necessarily be an inner exception but that's not what you should care about. What you need to know is the stack trace of where the exception originated.

Not every exception do actually have an inner exception. First check if inner ex is a null and if it is not then process it.

Having said this, you can of course re-throw your exception like below:

catch(Exception ex)
 {
     // so smth
     // ..
     // rethrow..

     throw;          
 }

But please remember two things:

  1. Do not type throw ex, just throw.

  2. Do it only if you really want to do something with this exception before rethrowing. If you don't have such a plan, just don't catch it on this level.

I would do something like:

try
{
  ...
}
catch (Exception ex)
{
  if (ex.InnerException == null)
    throw ex;
  else
    throw ex.InnerException;
}

then at some point where you want to do the stack trace, do something along the lines of:

StackTrace trace = new StackTrace(System.Threading.Thread.CurrentThread, true);
StackFrame[] frames = trace.GetFrames();
string result = string.Empty;
foreach (StackFrame sf in frames)
{
  string += sf.GetMethod().Name;
}
MessageBox(result);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top