1

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;
               }
          }
    
Nissa
  • 4,636
  • 8
  • 29
  • 37

4 Answers4

2

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.

Mike Dinescu
  • 54,171
  • 16
  • 118
  • 151
1

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;
}
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
Konrad Kokosa
  • 16,563
  • 2
  • 36
  • 58
  • 1
    you are going to lose the stack trace with this – David Pilkington Nov 16 '13 at 19:37
  • Yes, I was just adding this comment. – Konrad Kokosa Nov 16 '13 at 19:39
  • To be precise, the `StackTrace` property of the new exception will not contain the stack trace detail of the inner exception. However, the stack trace of the inner exception is still there in its own `StackTrace` property, so you're not losing anything if you know where to look for. – Chin Jan 21 '15 at 21:11
1

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.

Zegar
  • 1,005
  • 10
  • 18
  • Additionally, catching and simply re-throwing the exception would be a redundant, unnecessary step, unless you also want to do something else in the catch block. –  Nov 16 '13 at 19:37
0

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);
HypnoToad
  • 585
  • 1
  • 6
  • 18
  • --Or you could just use the exception's stacktrace. – HypnoToad Nov 16 '13 at 19:50
  • 1
    Ouch. DO NOT re-throw (inner) exceptions like this. You will loose its stacktrace and debugging will become a nightmare. And why would you be interested about the calling stack from some other part of your code? –  Nov 16 '13 at 20:56