3

Regarding the duplicated. I can access the Message property but not the Detail property even when I can see is part of the Exception object during debuging. So the question is Why cant access Detail property.

I catch an exception.

catch (Exception ex)
{
    string msg = ex.InnerException.InnerException.Message;
    // say Exception doesnt have Detail property
    // string detail = ex.InnerException.InnerException.Detail; 
    return Json(new { status = "Fail", message = msg, detail: detail });
}

ex doesnt say anthing enter image description here

ex.InnerException show same message enter image description here

ex.InnerException.InnerException. finally some real message, "db table duplicated key" enter image description here

ex.InnerException.InnerException.Message I can get the message. enter image description here

But cant get the Detail "the guilty key" even when there is one property Detail

enter image description here

  1. So how can I get the Detail?.
  2. Bonus: Why have to go deep InnerException twice to get some meaningfull message?
Juan Carlos Oropeza
  • 47,252
  • 12
  • 78
  • 118
  • Loop through the list of exceptions and try to cast to specific exception type nd then read Detail if cast is succesfull. – Wiktor Zychla Oct 25 '16 at 20:49
  • 1
    [This](http://stackoverflow.com/a/9314420/5095502) is the method I use. Trick is, `while (ex != null) { thisMsg = ex.Message; ex = ex.InnerException; }`. This way you iterate all inner exceptions no matter if there's 0 or a bunch. It's just up to you how to concatenate all the messages together into a single return or single message. – Quantic Oct 25 '16 at 20:49
  • 2
    @L.B Probably you are not downvoter or if you are, you are best of them because you left a comment. IMO reputation of the user is not a good reason for downvote. It's a question like other questions and seeing such amount of downvotes for this question is really strange! Maybe the user is a beginner in C#. – Reza Aghaei Oct 25 '16 at 20:55
  • Possible duplicate of [Getting all messages from InnerException(s)?](http://stackoverflow.com/questions/9314172/getting-all-messages-from-innerexceptions) edit: That was flagged. As for his second question, the duplicate would probably be [here](http://stackoverflow.com/questions/22826067/what-is-inner-exception), but because you aren't supposed to ask two questions in one, I think I'm not supposed to flag it as a duplicate of two questions. – Quantic Oct 25 '16 at 20:57
  • @L.B No need the sarcasm. If you check my profile, more of my reps are from SQL not C#. Also if you see I already debug the code and couldnt found how to get it. – Juan Carlos Oropeza Oct 25 '16 at 21:00
  • You are right @RezaAghaei my rep points are from SQL. – Juan Carlos Oropeza Oct 25 '16 at 21:01
  • @Quantic Im not sure if I have multiple exception. My problem is the one I got show me a property `Detail` while debuging but cant get it. – Juan Carlos Oropeza Oct 25 '16 at 21:14
  • The `Detail` property belongs to a specific type of exception. For example `SqlException` has an `ErrorCode` property. In such cases to extract suitable message from the exception you need a method that perform a suitable action to extract information based on exception type. You can create such method once and use it all times. For example when you receive a `DbUpdateException`, you can check its `InnerException` for more details and if the inner exception is of `SqlException` type, you can use `ErrorCode` and `Message` property of the inner exception. – Reza Aghaei Oct 25 '16 at 21:18
  • OK I think I see your problem. `Detail` is not a property that is part of the [`Exception`](https://msdn.microsoft.com/en-us/library/system.exception(v=vs.110).aspx) class. You are working with code that must have created their own Exception type that derived from `Exception`. You have to cast the exception to the custom type to access the custom `Detail` property. `Exception ex` does not have `ex.Detail` as a property you can access. – Quantic Oct 25 '16 at 21:18
  • In general, such types of exceptions are not for end users and those are for programmers and for debug purpose. Instead of showing the exception message to end user, usually it's better to just log the exception and stack trace and show a general/suitable error message to end user. IMO creating suitable error messages for end users is somehow an art and needs efforts. But as a start point you can create a exception translation service which tries to extract friendly messages based on exceptions. – Reza Aghaei Oct 25 '16 at 21:24
  • @RezaAghaei That is exactly what Im trying to do. This is a webservice to connect to my db. In this case user try to insert a record already in db I want show the right message. The message is clear, but the detail show what is the specific row with the error, that is why I want include it. – Juan Carlos Oropeza Oct 25 '16 at 21:26
  • I see. If you show the message *The INSERT statement conflicted with the REFERENCE constraint "ConstraintName". The conflict occurred in database "DatabaseName", table "SchemaName.TableName", column 'ColumnName'.* It's not useful for end user. But if you want to show it, you can. I prefer to check if the `SqlException.ErrorCode` is `547` then show a general message to make the user check the values which they entered for foreign keys. Also you can create a key/value map for `ConstraintName` and show more suitable message for `547` error code based on different constraint names. – Reza Aghaei Oct 25 '16 at 21:34
  • @RezaAghaei This isnt for end user, is for the web developer using the webservice. But I see your point – Juan Carlos Oropeza Oct 25 '16 at 21:36

8 Answers8

6

I think the most elegant way to do this now is using C# 6 when keyword in a catch statement and C# 7 is operator.

try
{
    //your code
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException pex)
{
    string msg = pex.Message;
    string detail = pex.Detail; 
    return Json(new { status = "Fail", message = msg, detail: detail });
}
Eugene Chybisov
  • 1,634
  • 2
  • 23
  • 32
3
  1. The trick is to recognize the type of exception being thrown and cast the General Exception to the correct Type where you will then have access to extended properties for that Exception type.

for example:

if (processingExcption is System.Data.Entity.Validation.DbEntityValidationException)
{
    exceptionIsHandled = true;
    var entityEx = (System.Data.Entity.Validation.DbEntityValidationException)processingExcption;
    foreach (var item in entityEx.EntityValidationErrors)
    {
        foreach (var err in item.ValidationErrors)
            returnVal.Invalidate(SystemMessageCategory.Error, err.ErrorMessage);
    }
}
else if (processingExcption is System.Data.SqlClient.SqlException && ((System.Data.SqlClient.SqlException)processingExcption).Number == -2)//-2 = Timeout Exception
{
    exceptionIsHandled = true;
    returnVal.Invalidate(SystemMessageCategory.Error, "Database failed to respond in the allotted time. Please retry your action or contact your system administrator for assistance.", 
        messageCode: Architecture.SystemMessage.SystemMessageCode.DBTimeout);
}
  1. The fact that the detail you are looking for is 2 inner exceptions deep is incidental. Depending on how many times the exception is caught and wrapped will determine how deep the exception you care about is - your best bet is to iterate through the exception stack looking for exception types you wish to handle.
Theo
  • 885
  • 6
  • 16
  • Thanks Theo, Finally something with I can work. I was start feeling sad for so many downvotes. So even when while debuging I can see the `Detail` property I cant access until I cast to something else? – Juan Carlos Oropeza Oct 25 '16 at 21:07
  • Yup. The Detail is likely specific to whatever exception type that is. You would have to cast it to that type to access that property. That snippet is from a recursive exception check I have to pull out details from Entity Framework Validation Exceptions that occur and are subsequently wrapped with a different exception type. NOTE: Wherever possible, it's best to not `catch(Exception)` but rather `catch(System.Data.Entity.Validation.DbEntityValidationException)`, but when the exception is wrapped the type is wrong so you are stuck with the recursive check. – Theo Oct 25 '16 at 21:09
  • I found the solution, but I couldnt catch a specific Exception, still have to catch the generic one. How I know what is the specific Exception for `ex`? – Juan Carlos Oropeza Oct 25 '16 at 21:47
  • Ok, I think I found the type now ;) – Juan Carlos Oropeza Oct 25 '16 at 21:50
3

Referring to your own answer I commented, you definitely should be much more defensive, otherwise you risk of a null reference exception from within your catch clause.

catch (Exception ex)
{
    string Detail = string.Empty;

    while ( ex != null ) 
    {
        if ( ex is Npgsql.NpgsqlException )
        {
            // safe check
            Npgsql.NpgsqlException ex_npg = (Npgsql.NpgsqlException)ex;
            Details = ex_npg.Detail;
        }
        // loop
        ex = ex.InnerException;
    }

    // warning, Detail could possibly still be empty!
    return Json(new { status = "Fail", detail = Detail });
}
Wiktor Zychla
  • 47,367
  • 6
  • 74
  • 106
0
  1. You cannot get details more than found in this exception

To show real exception loop over innerexceptions until it is null. Then you reached the first one

  1. The exception was thrown from a source class or function then readed by upper level class that throw it with more global details because there is no error handling on the source
Hadi
  • 36,233
  • 13
  • 65
  • 124
0

Well, it's very sad, but the inner exception is not a magic stick. Usually it's just an object that author of the code that you call puts as the second parameter of the Exception constructor. So, the general answer: "no way". But debugger sometimes could help :). I would say - call stack of the exception usually more descriptive the InnerException.

olk
  • 199
  • 1
  • 2
  • 8
0

A quick solution would be to click on the "Detail" property in the "Quick Watch" window. Your answer will be in "Expression" texbox at the top of the quick watch window. Example, the expression for Postgres duplicate detail is:

((Npgsql.PostgresException)ex.InnerException.InnerException).Detail
Llewelyn Jones
  • 161
  • 1
  • 5
0

Here is my function to get some more info from Postgres exception

catch (Exception ex)   {
                // Get PGSQL exception info              
                var msg = ExceptionMessage (ex);
}

public static string            ExceptionMessage            (Exception ex) {
            string msg = ex.Message;

            var pgEx = ex as PostgresException;
            if (pgEx != null) { 
                msg = pgEx.Message;
                msg += pgEx.Detail != null ? "\n"+pgEx.Detail.ToStr() : "";
                msg += pgEx.InternalQuery != null ? "\n"+pgEx.InternalQuery.ToStr() : "";
                msg += pgEx.Where != null ? "\n"+ pgEx.Where : "";
            }

            return msg;
        }
Maciej
  • 10,423
  • 17
  • 64
  • 97
0

Thanks Maciej this solution is great to intercept PostgreSQL Errors

Only correction I did on this

msg += pgEx.Detail != null ? "\n"+pgEx.Detail.ToStr() : "";
msg += pgEx.InternalQuery != null ? "\n"+pgEx.InternalQuery.ToStr() : "";

instead

msg += pgEx.Detail != null ? "\n" + pgEx.Detail.ToString() : "";
msg += pgEx.InternalQuery != null ? "\n" + pgEx.InternalQuery.ToString() : "";
buddemat
  • 4,552
  • 14
  • 29
  • 49
BoS
  • 9
  • 1
  • 5