how to get Sql Exception in VIewBag.Message - asp.net MVC - c#

I am trying to catch the exception message and alert the user
so when I use the exception it works but with a generic message "please see inner exception for details" What I want is display the sql exception instead for example duplicate key in my scenario I want to receive a message from a trigger.
try {
if (ModelState.IsValid)
{
db.Entry(investor).State = EntityState.Modified;
db.SaveChanges();
if (amount != 0)
{
InvestorPeriod ip = new InvestorPeriod();
ip.InvestorID = investor.InvestorID;
ip.Amount = amount;
ip.RemainingAmount = amount;
ip.InvestorPeriodStatusID = 1;
db.InvestorPeriods.Add(ip);
db.SaveChanges();
}
}
}
catch (SqlException ex)
{
// I want to display the sqlexception instead of the "please see inner exception for details" and then display it a dialog
ViewBag.Error = ex.Message;
}

.NET has a method for automatically retrieving inner exceptions (which is what you're being asked to do) - Exception.GetBaseException(). So you can simply change your last line to:
ViewBag.Error = ex.GetBaseException().Message;
Sometimes you do want to get all the exception messages, in which case you have to loop through the InnerExceptions (or flatten them if the exception is an AggregateException)

Here is a sample code how to get all inner exceptions
catch (SqlException ex)
{
// All message will be stored here.
var exceptionMessages = new List<string>();
exceptionMessages.Add(ex.Message);
while(ex.InnerException != null)
{
ex = ex.InnerException;
exceptionMessages.Add(ex.Message);
}
// Add messages to ViewBag
ViewBag.Error = string.Join(" ", exceptionMessages);
}

Related

How do I catch the right exception?

I have the method below. Anytime myResult variable is empty, I get a 500 internal server
error. Various scenarios can make myResult empty.
How do I catch the right exception and not a 500 internal server error ?
try
{
var myResult = await _myRepository.GetDataAsync(Id);
if (!myResult.Any())
{
throw new ArgumentException("Unable to retrieve record");
}
return myResult;
}
catch (HttpProxyException ex)
{
string errorMessage = ex.Message.ToString();
throw new ArgumentException(errorMessage);
}
I suspect your _myRepository.GetDataAsync(Id); to throw the HttpProxyException. So you can't check if myResult is empty because the error throw earlier.
You can adapt your code as follow :
var myResult;
try
{
myResult = await _myRepository.GetDataAsync(Id);
catch (HttpProxyException ex)
{
string errorMessage = ex.Message.ToString();
throw new ArgumentException(errorMessage);
}
if (!myResult.Any())
{
throw new ArgumentException("Unable to retrieve record");
}
return myResult;
In this scenario, the 500 internal server error throw only when you try to get the data but received the error. If you succeed to get the result but there is no datas, you throw your custom exception.
As it stands, your code will only catch HttyPRoxyException type exceptions. So your ArgumentException exception it won't be caught.
But you can have multiple catch clauses to handle different exception types. They are evaluated in order from first to last, and the first one with a matching signature will handle it.
For example you could use
try
{
var myResult = await _myRepository.GetDataAsync(Id);
if (!myResult.Any())
{
throw new ArgumentException("Unable to retrieve record");
}
return myResult;
}
catch (HttpProxyException ex)
{
string errorMessage = ex.Message.ToString();
throw new ArgumentException(errorMessage);
}
catch (ArgumentException ex)
{
//Do something different here;
}
You could also put a generic catch at the end to trap any other Exception types, if desired.
catch (Exception ex)
{
//Do whatever;
}
NB: One thing to be aware of is that by throwing a new Exception in the Catch, you will be losing the earlier stack trace. This can make it harder to pinpoint the exact location of the offending code.
One way to help with this problem is to include the original exception with the new one, i.e.
catch (HttpProxyException ex)
{
string errorMessage = ex.Message.ToString();
throw new ArgumentException(errorMessage, ex);
}

How get Detail of InnerException

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
ex.InnerException show same message
ex.InnerException.InnerException. finally some real message, "db table duplicated key"
ex.InnerException.InnerException.Message I can get the message.
But cant get the Detail "the guilty key" even when there is one property Detail
So how can I get the Detail?.
Bonus: Why have to go deep InnerException twice to get some meaningfull message?
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 });
}
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);
}
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.
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 });
}
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
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
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.
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
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;
}
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() : "";

Why are some exceptions a few levels deep?

I have a simple entity insert as follows:
using (var db = new MyContext())
{
var item = new Artist();
TryUpdateModel(item);
if (ModelState.IsValid)
{
db.Artist.Add(item);
try
{
db.SaveChanges();
gvArtist.DataBind();
gvArtist.Visible = true;
}
catch (Exception e)
{
Master.Warning = e.InnerException.InnerException.Message;
}
}
}
e.Message and e.InnerException.Message both equate to:
"An error occurred while updating the entries. See the inner exception for details."
But, e.InnerException.InnerException.Message gives the exception I'm looking for, which is:
"Violation of UNIQUE KEY constraint 'UQ_artist_Cuid'. Cannot insert duplicate key in object 'dbo.artist'. The duplicate key value is (11). The statement has been terminated."
I'm worried about missing other exceptions, or causing an exception if I just keep
Master.Warning = e.InnerException.InnerException.Message;
in play.
Your fears are completely founded. Something like this is what you're looking for.
catch (Exception ex)
{
while (ex.InnerException != null)
{
ex = ex.InnerException;
}
Master.Message = ex.Message;
}
The reason for the errors being varying levels deep is that the errors can occur in different sections of code, and they may bubble up through a varying number of methods that wrap them inside other exceptions. You can't plan for them to come from a specific level.

How to handle exception from specific database error

I am trying to create a transaction like so:
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required,
options))
{
try
{
dbContext.MyTable.PartnerId = someGuid;
dbContext.SaveChanges();
scope.Complete();
dbContext.AcceptAllChanges()
}
catch (Exception ex)
{
log.LogMessageToFile("Exception - ExceptionType: " +
ex.GetType().ToString() + "Exception Messsage: " + ex.Message);
}
}
I know if I try to insert an item manully in sql with a duplicate in a specific column, I get the following error from sql:
Cannot insert duplicate key row in object 'dbo.MyTable' with unique index 'idx_PartnerId_notnull'. The duplicate key value is (7b072640-ca81-4513-a425-02bb3394dfad).
How can I programatically catch this exception specifically, so I can act upon it.
This is the constraint I put on my column:
CREATE UNIQUE NONCLUSTERED INDEX idx_yourcolumn_notnull
ON YourTable(yourcolumn)
WHERE yourcolumn IS NOT NULL;
Try this:
try {
}
catch (SqlException sqlEx) {
}
catch (Exception ex) {
}
SQL errors and warnings that happen on the server side are caught in this exception.
Read about it here:
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlexception(v=vs.110).aspx
The above answer would allow you to catch the SqlException, but you would need to further refine the handling within the 'SqlException' catch block if you only want to inform the user of a particular error. The SqlException class has a property of 'ErrorCode' from which you can derive the actual error being produced by the server. Try doing something like below:
try
{
}
catch (SqlException sqlEx)
{
if(sqlEx.ErrorCode == 2601)
{
handleDuplicateKeyException();
}
}
2601 is the actual error code produced by SQL Server for you particular error. For a full list just run the SQL:
SELECT * FROM sys.messages
Use SqlException's number property.
For duplicate error the number is 2601.
catch (SqlException e)
{
switch (e.Number)
{
case 2601:
// Do something.
break;
default:
throw;
}
}
List of error codes
SELECT * FROM sysmessages
You can catch it by its type:
try
{
// ...
}
catch (SpecialException ex)
{
}
catch (Exception ex)
{
}
EDIT: According to Ivan G's answer, you will get an SqlException, which has an error ErrorCode property that probably specific. So you have to check the error code for this type of error.
you can check exception text or it's other parameters when it is thrown, so then you can act like you wan conditionally
like :
catch(SqlException ex)
{
if(ex.Message.Contains("Cannot insert duplicate key row in object"))
{
}
}
or exception number like
catch(SqlException ex)
{
switch (ex.Number)
{
case : someNumber:
{
//..do something
break...;
}
}
}

How to determine if an exception is of a particular type

I have a piece of try catch code:
try
{
...
}
catch(Exception ex)
{
ModelState.AddModelError(
"duplicateInvoiceNumberOrganisation", "The combination of organisation and invoice number must be unique");
}
For this piece of code I'm trying to insert a record into a database: The dba has set it up so that the database checks for duplicates and returns an error if there are duplicates. Currently, as you can see, I'm adding the same error to the model no matter what error occurred. I want it changed so this error is only added to the model if it was caused by the duplicate error set up by the dba.
Below is the error I want to catch. Note it's in the inner exception. Can anyone tell me how to specifically catch this one?
before your current catch add the following:
catch(DbUpdateException ex)
{
if(ex.InnerException is UpdateException)
{
// do what you want with ex.InnerException...
}
}
From C# 6, you can do the following:
catch(DbUpdateException ex) when (ex.InnerException is UpdateException)
{
// do what you want with ex.InnerException...
}
Replace System.Threading.ThreadAbortException with your exception.
try
{
//assume ThreadAbortException occurs here
}
catch (Exception ex)
{
if (ex.GetType().IsAssignableFrom(typeof(System.Threading.ThreadAbortException)))
{
//what you want to do when ThreadAbortException occurs
}
else
{
//do when other exceptions occur
}
}
Not enough rep to comment. In response to #conterio question (in #Davide Piras answer):
is there a catch "when not" syntax?
There is.
catch (Exception e) when (!(e is ArgumentException)) { }
To get name of the exception you can use
catch (Exception exc){
if (exc.GetType().FullName == "Your_Exception")
{
// The same can be user for InnerExceptions
// exc.InnerException.GetType().FullName
}
}
You can take a look at the SQLException class -- and check for the contents of the exception's message if it contains what you now see in your inner exception..Something like this:
try
{
//your code here
}
catch (SQLException ex)
{
if (ex.Message.Contains("Cannot insert duplicate key in obj...."))
{
//your code here
}
}

Categories