I'm trying to show a Duplicate Record error message in an WebAPI service. I'm using .net5 and mysql. I cannot seem to find a way to pull the duplicateEntry message from the exception response. I'd like to key in on the ErrorCode field and use this to tailor a response to the user. I can see the Message property, but cannot figure out how to access the innerException.
{
try
{
module.Id = Guid.NewGuid();
await _moduleRepository.Add(module);
await _uow.CompleteAsync();
return true;
}
catch (DbUpdateException ex)
{
logger.LogWarning("Module - Add Error: " + ex.Message);
return false;
}
}
You may try to catch an Exception object which has a InnerException attribute. Also, you may also check DbEntityValidationException class.
Reference: link
Found something that worked. Made a helper function, but this is highly coupled to Mysql. Had to check the InnerException to see if it was of type MsyqlException.
{
public static string GetExceptionMessage(Exception ex)
{
var message = ex.Message;
var innerException = ex.InnerException;
if (innerException != null && (innerException.GetType() == typeof(MySqlException)))
{
var dbException = (MySqlException)innerException;
if(dbException.ErrorCode == MySqlErrorCode.DuplicateKeyEntry)
{
return ErrorMessages.RecordExists;
} else
{
return ErrorMessages.UnknownError;
}
}
return message;
}
}
Related
I'm trying to grab a ComponentId of a network device, it all works but now I am trying to grab a catch from the GetKey() method. I'm just not quite sure how to forward it (or if this is even the correct way). Should I even be using try/catch in methods?
static string GetKey()
{
try
{
using (RegistryKey Adapter = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}", true))
{
foreach (string Keyname in Adapter.GetSubKeyNames())
{
RegistryKey Key = Adapter.OpenSubKey(Keyname);
if (Key.GetValue("DriverDesc").ToString() != "somename")
{
return Key.GetValue("ComponentId").ToString();
}
return null;
}
return null;
}
}
catch (Exception ex)
{
return ex.Message;
}
}
static void Main(string[] args)
{
if (GetKey() == null)
{
Console.WriteLine("null");
}
else if () // if catch throws exception?
{}
else
{
Console.WriteLine(GetKey());
}
Console.ReadKey();
}
This looks like a very bad idea to me to return the exception message in a method that returns a string (which is some key).
You need to understand: what is the reason for the try/catch? What are you trying to achieve there? If you just want to log the exception, it's better to do that in some top-level framework error handling than here.
If you want to hide some exceptions (which is generally a bad idea, but there are exceptions), then you can just return null there to indicate nothing has been found. Moreover it's quite unlikely that the exception happens after you have the value of the key you can return (or even impossible since you just return it).
It is also not 100% clear what exactly you want to forward. If it's the exception itself, then you can do this:
try
{
}
catch (Exception exception)
{
throw;
}
Now this will properly rethrow the exception with original stack trace. While this code is useless on it's own, you can add some handling before throw if you need.
Try, thats what the try, catch, finally is for
Try
{
String result = GetKey();
if (result == null)
{
Console.WriteLine("null");
}
else
{
Console.WriteLine(result);
}
}
catch
{
Console.WriteLine("Reading key failed");
}
Console.ReadKey();
Note also I changed your code from repeatedly calling GetKey - which was unnecessary
Returning an exception message when the code clearly expects some key value is a very bad idea.
The typical way to do it is to rethrow the exception as your application-specific type, with the original exception as inner. So in your catch block in the method you do:
catch (Exception ex)
{
throw new MyApplicationException(ex);
}
Where MyApplicationException is your own exception type that you then handle in your main program using
catch (MyApplicationException ex)
{
////Handle it!
}
You could write your GetKey in a bit more usable fashion with making it a TryGetKey instead just GetKey and handle exceptions. Like so
static bool TryGetKey(out string result)
{
result = null;
try
{
using (RegistryKey Adapter = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Class\{4d36e972-e325-11ce-bfc1-08002be10318}", true))
{
foreach (string Keyname in Adapter.GetSubKeyNames())
{
RegistryKey Key = Adapter.OpenSubKey(Keyname);
if (Key.GetValue("DriverDesc").ToString() != "somename")
{
result = Key.GetValue("ComponentId").ToString();
return true;
}
}
}
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message);
}
return false;
}
And in your calling code you should be checking what the result of that function was in order to do something with the key or handle the failure.
var key = string.Empty;
if (TryGetKey(out key))
{
// Got key can do something.
}
else
{
// Something went wrong, what should the application do?
}
Also generally wrapping the entire code block within try-catch is considered bad practice.
One of my tables have a unique key and when I try to insert a duplicate record it throws an exception as expected. But I need to distinguish unique key exceptions from others, so that I can customize the error message for unique key constraint violations.
All the solutions I've found online suggests to cast ex.InnerException to System.Data.SqlClient.SqlException and check the if Number property is equal to 2601 or 2627 as follows:
try
{
_context.SaveChanges();
}
catch (Exception ex)
{
var sqlException = ex.InnerException as System.Data.SqlClient.SqlException;
if (sqlException.Number == 2601 || sqlException.Number == 2627)
{
ErrorMessage = "Cannot insert duplicate values.";
}
else
{
ErrorMessage = "Error while saving data.";
}
}
But the problem is, casting ex.InnerException to System.Data.SqlClient.SqlException causes invalid cast error since ex.InnerException is actually type of System.Data.Entity.Core.UpdateException, not System.Data.SqlClient.SqlException.
What is the problem with the code above? How can I catch Unique Key Constraint violations?
With EF6 and the DbContext API (for SQL Server), I'm currently using this piece of code:
try
{
// Some DB access
}
catch (Exception ex)
{
HandleException(ex);
}
public virtual void HandleException(Exception exception)
{
if (exception is DbUpdateConcurrencyException concurrencyEx)
{
// A custom exception of yours for concurrency issues
throw new ConcurrencyException();
}
else if (exception is DbUpdateException dbUpdateEx)
{
if (dbUpdateEx.InnerException != null
&& dbUpdateEx.InnerException.InnerException != null)
{
if (dbUpdateEx.InnerException.InnerException is SqlException sqlException)
{
switch (sqlException.Number)
{
case 2627: // Unique constraint error
case 547: // Constraint check violation
case 2601: // Duplicated key row error
// Constraint violation exception
// A custom exception of yours for concurrency issues
throw new ConcurrencyException();
default:
// A custom exception of yours for other DB issues
throw new DatabaseAccessException(
dbUpdateEx.Message, dbUpdateEx.InnerException);
}
}
throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException);
}
}
// If we're here then no exception has been thrown
// So add another piece of code below for other exceptions not yet handled...
}
As you mentioned UpdateException, I'm assuming you're using the ObjectContext API, but it should be similar.
In my case, I'm using EF 6 and decorated one of the properties in my model with:
[Index(IsUnique = true)]
To catch the violation I do the following, using C# 7, this becomes much easier:
protected async Task<IActionResult> PostItem(Item item)
{
_DbContext.Items.Add(item);
try
{
await _DbContext.SaveChangesAsync();
}
catch (DbUpdateException e)
when (e.InnerException?.InnerException is SqlException sqlEx &&
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
{
return StatusCode(StatusCodes.Status409Conflict);
}
return Ok();
}
Note, that this will only catch unique index constraint violation.
try
{
// do your insert
}
catch(Exception ex)
{
if (ex.GetBaseException().GetType() == typeof(SqlException))
{
Int32 ErrorCode = ((SqlException)ex.InnerException).Number;
switch(ErrorCode)
{
case 2627: // Unique constraint error
break;
case 547: // Constraint check violation
break;
case 2601: // Duplicated key row error
break;
default:
break;
}
}
else
{
// handle normal exception
}
}
// put this block in your loop
try
{
// do your insert
}
catch(SqlException ex)
{
// the exception alone won't tell you why it failed...
if(ex.Number == 2627) // <-- but this will
{
//Violation of primary key. Handle Exception
}
}
EDIT:
You could also just inspect the message component of the exception. Something like this:
if (ex.Message.Contains("UniqueConstraint")) // do stuff
I thought it might be useful to show some code not only handling the duplicate row exception but also extracting some useful information that could be used for programmatic purposes. E.g. composing a custom message.
This Exception subclass uses regex to extract the db table name, index name, and key values.
public class DuplicateKeyRowException : Exception
{
public string TableName { get; }
public string IndexName { get; }
public string KeyValues { get; }
public DuplicateKeyRowException(SqlException e) : base(e.Message, e)
{
if (e.Number != 2601)
throw new ArgumentException("SqlException is not a duplicate key row exception", e);
var regex = #"\ACannot insert duplicate key row in object \'(?<TableName>.+?)\' with unique index \'(?<IndexName>.+?)\'\. The duplicate key value is \((?<KeyValues>.+?)\)";
var match = new System.Text.RegularExpressions.Regex(regex, System.Text.RegularExpressions.RegexOptions.Compiled).Match(e.Message);
Data["TableName"] = TableName = match?.Groups["TableName"].Value;
Data["IndexName"] = IndexName = match?.Groups["IndexName"].Value;
Data["KeyValues"] = KeyValues = match?.Groups["KeyValues"].Value;
}
}
The DuplicateKeyRowException class is easy enough to use... just create some error handling code like in previous answers...
public void SomeDbWork() {
// ... code to create/edit/update/delete entities goes here ...
try { Context.SaveChanges(); }
catch (DbUpdateException e) { throw HandleDbUpdateException(e); }
}
public Exception HandleDbUpdateException(DbUpdateException e)
{
// handle specific inner exceptions...
if (e.InnerException is System.Data.SqlClient.SqlException ie)
return HandleSqlException(ie);
return e; // or, return the generic error
}
public Exception HandleSqlException(System.Data.SqlClient.SqlException e)
{
// handle specific error codes...
if (e.Number == 2601) return new DuplicateKeyRowException(e);
return e; // or, return the generic error
}
If you want to catch unique constraint
try {
// code here
}
catch(Exception ex) {
//check for Exception type as sql Exception
if(ex.GetBaseException().GetType() == typeof(SqlException)) {
//Violation of primary key/Unique constraint can be handled here. Also you may //check if Exception Message contains the constraint Name
}
}
You have to be very specific while writing the code.
try
{
// do your stuff here.
{
catch (Exception ex)
{
if (ex.Message.Contains("UNIQUE KEY"))
{
Master.ShowMessage("Cannot insert duplicate Name.", MasterSite.MessageType.Error);
}
else { Master.ShowMessage(ex.Message, MasterSite.MessageType.Error); }
}
I have just updated the above code a bit and its working for me.
I need to know how to catch and recognize timeout error in comparison to other WebException errors. Request timeout is set to "1" to make environment to be able to catch the exception. I just need to know how to recognize it. (i.e. default working value = 60000). Here is my code:
// some code here
request.Timeout = 1;
// some code here
catch (WebException wex)
{
Console.WriteLine(wex);
try
{
response_code = ((int)((HttpWebResponse)wex.Response).StatusCode);
State_show.ForeColor = System.Drawing.Color.Red;
if (response_code == 404)
{
State_show.Text = "Error 404. Retrying the request";
request_1();
}
if (response_code != 400 || response_code != 503 || response_code != 404)
{
State_show.Text = "Error " + response_code + ". Please try again";
FlashWindow.Flash(this);
}
}
catch (Exception exc)
{
Console.WriteLine(exc);
MessageBox.Show("Check internet connection");
}
}
So it catches good if I received bad http status code. But it throws additional exception if response has timed out. The simplest way is to get
string wex_modified = wex.ToString();
If (wex_modified.contains("Timeout"))
{
// some handling here
}
But I don't really like it. I tried to use wex.GetType() and other available functions, but without success.
Is there any other way to recognize the exception?
The WebException.Status property returns a WebExceptionStatus enum. One of the enumeration values is Timeout.
if (wex.Status == WebExceptionStatus.Timeout)
{
// We have a timeout!
}
I am trying to simplify error handling in my client application which consumes a ServiceStack REST service using the JsonServiceClient.
My custom exceptions that I throw on the server are serialised in the ResponseStatus object, and I can see a WebServiceException is thrown.
But at the moment I am having to check for my exception types, by matching the WebServiceException ErrorCode to the type name of my exception class. (Which is exposed in the shared DTO class):
/** Current Method **/
try {
client.Get(new RequestThatWillFail());
} catch(WebServiceException ex) {
if(ex.ErrorCode == typeof(ValidationFailedException).Name)
Console.WriteLine("Validation error");
else if(ex.ErrorCode == typeof(UnauthorizedException).Name)
Console.WriteLine("Not logged in");
else if(ex.ErrorCode == typeof(ForbiddenException).Name)
Console.WriteLine("You're not allowed to do that!");
else
throw; // Unexpected exception
}
Ideally I was hoping that JsonServiceClient would contain some helper method or overridable conversion function that would allow me to translate the WebServiceException to my known exception type; So that I could use my try ... catch in a more traditional way:
/** Ideal Method **/
try {
client.Get(new RequestThatWillFail());
} catch(ValidationFailedException ex) { // (WebServiceException is converted)
Console.WriteLine("Validation error");
} catch(UnauthorizedException ex) {
Console.WriteLine("Not logged in");
} catch(ForbiddenException ex) {
Console.WriteLine("You're not allowed to do that!");
}
Update (for clarification)
I have exceptions working, I can debug, and get all the information I need to.
But I would like to ultimately be able to catch my own exception instead of the generic WebServiceException
I am not looking to extend additional properties on the exception, it's ultimately a convenience of not having to do lots of typeof(MyException).Name == ex.ErrorCode within the catch.
I would envisage being able to provide JsonServiceClient with a map of:
{ Type typeof(Exception), string ErrorCode }
i.e. Something like
JsonServiceClient.MapExceptionToErrorCode = {
{ typeof(BadRequestException), "BadRequestException" },
{ typeof(ValidationFailedException), "ValidationFailedException" },
{ typeof(UnauthorizedException), "UnauthorizedException" },
{ typeof(AnotherException), "AnotherException" }
// ...
}
Similar to how the server currently maps exceptions to Http status codes.
Then the ThrowWebServiceException<TResponse> and the HandleResponseError<TResponse> within the JsonServiceClient could look the ErrorCode up in the map and if it matches, return a new Exception of that type, passing the WebServiceException as a parameter, or alternatively Translate the properties.
But with the ultimate goal of throwing a more useable error. If there wasn't a match, go ahead and continue throwing the WebServiceException.
I'd override ThrowWebServiceException<TResponse> and the HandleResponseError<TResponse> but I don't think this is possible. And I don't wan't to build my own version to provide this functionality.
I hope I have explained this OK.
My approach for exception handling is to do in service side what is described in the Structured Error Handling and Overriding the default Exception handling
I use my ServiceRunner and I override the HandleException.
If my API exception is thrown then I create a custom response.
public override object HandleException(IRequestContext requestContext,T request,
Exception ex)
{
APIException apiex = ex as APIException; // custo application exception
if (apiex != null)
{
ResponseStatus rs = new ResponseStatus("APIException", apiex.message);
rs.Errors = new List<ResponseError>();
rs.Errors.Add(new ResponseError());
rs.Errors[0].ErrorCode = apiex.errorCode.ToString();
rs.Errors[0].FieldName = requestContext.PathInfo;
rs.Errors[1].ErrorCode = apiex.detailCode.ToString();
// create an ErrorResponse with the ResponseStatus as parameter
var errorResponse = DtoUtils.CreateErrorResponse(request, ex, rs);
Log.Error("your_message", ex); // log only the the error
return errorResponse;
}
else
return base.HandleException(requestContext, request, ex);
}
UPDATE :
In client side, I create an wrapper for the service call and in WebServiceException,
I check the ResponseStatus.Errors. In case of my error code, then I rethrow my exception.
T ServiceCall<T>(string command, string rest_uri, object request)
{
try
{
if (command == "POST")
return client.Post<T>(serverIP+rest_uri, request);
}
catch (WebServiceException err)
{
if (err.ErrorCode == "APIException" && err.ResponseStatus.Errors != null
&& err.ResponseStatus.Errors.Count > 0)
{
string error_code = err.ResponseStatus.Errors[0].ErrorCode;
string path_info = err.ResponseStatus.Errors[0].FieldName;
string detail_error = err.ResponseStatus.Errors[1].ErrorCode;
throw new APIException(error_code,detail_error,path_info);
}
} finally {}
}
I have the following method in the class ProductServices:
public bool IsDownloadAllowed(int Id, string productId)
{
if (CustomStringFunctions.IsGuid(productId))
{
//Do Something
}
else
{
throw new FormatException("The Guid must be a valid Guid!");
}
}
If I use the method in the following set of instructions:
var _productServices = new ProductServices();
try
{
var flag = _productServices.IsDownloadAllowed(Id, productId);
//do something
}
catch (Exception e)
{
//handle exception
}
The exception is not caught by the catch statement. I also tried to replace Exception with FormatException with no luck. What am I doing wrong?
You must have mute exception in this code
if (CustomStringFunctions.IsGuid(productId))
{
//Do Something
}
You must be sure that you throw exception when occured (In the Do Something)
Sample of mute exception
Try
{
}
Catch(Exception ex)
{
//throw don't exist
}
Your code looks correct. A possible explanation for your problem is that CustomStringFunctions.IsGuid is incorrectly returning true, so the \\do something branch is executing instead of the exception-throwing branch.