Application specific exception wrapping "MongoDuplicateKeyException" not catch'ed - c#

Out of need have created application exception which wraps a MongoDuplicateKeyException and throwing that exception like below
Public class AppException : Exception
{
// all constructor implementation
public int ErrorCode { get; set; }
public string AppMessage { get; set; }
}
In method catching and throwing exception
public async Task<Response> Method1(parameter ...)
{
try
{
//some insert/update operation to DB
return <instance of Response>;
}
catch(MongoduplicateKeyException ex)
{
var exception = new AppException(ex.Message, ex)
{
ErrorCode = 22,
AppMessage = "some message",
};
throw exception;
}
}
Method that calls Method1() above
try
{
//some other operation
var response = await Method1();
}
catch(AppException ex)
{
SomeOtherLoggingMethod(ex, other parameter);
}
catch(Exception ex)
{
SomeMethod(ex, other parameter);
}
Surprisingly the catch(AppException ex) catch block never gets catched even though am throwing an AppException from Method1(). It always catch the generic catch block catch(Exception ex).
After debugging, found that in catch(Exception ex) catch block the exception type ex.GetType() is actually a WriteConcernException type (MongoduplicateKeyException : WriteConcernException).
So essentially that specific catch block not hitting cause the exception type is not AppException rather WriteConcernException But
Not sure why is it so? am I missing something obvious here? Please suggest.

You found the answer while debugging. The catch(AppException ex) block is not executed because public async Task<Response> Method1 does not throw an AppException it throws a WriteConcernException.
The API shows a WriteConcernException is the superclass of DuplicateKeyException so the catch block in Method1 is not hit and the exception bubbles up to the 2nd catch block in the caller.
So if you update your code to catch the appropriate exception it should work as you intend.
public async Task<Response> Method1(parameter ...)
{
try
{
//some insert/update operation to DB
return <instance of Response>;
}
catch (MongoServerException mse)
...

Related

How do i catch or throw an exception in asp.net core?

I'm kind of not sure as to where to catch an application and any other unexpected exception, but i do want to show on the front end which exception occurred either an application or any other exception.
If i just 'throw' from service manager then it will be catched in the controller, but what if there was an exception in the service manager and the controller?
This also seems verboose.
This is my service manager where I'm calling an API.
public async Task<int> CreateCategory(CategoryViewModel model)
{
logger.LogInformation("In {service}, Creating {CategoryModel}", nameof(CategoryServiceManager), model.ToString());
try
{
model.Guard(model.ToString());
int categoryId = await apiClient.PostAsync<int, CategoryViewModel>("Category", model);
return categoryId;
}
// Guard wil throw
catch (ApplicationException ex)
{
logger.LogError("Exception thrown for {model}: {Message}, {Stacktrace}", model.ToString(),ex.Message, ex.StackTrace);
throw new ApplicationException($"Exception thrown in service when creating category: {ex.Message}");
}
catch (Exception ex)
{
logger.LogError("Unexpected error thrown in service when creating a category : {Message}, {Stacktrace}", ex.Message, ex.StackTrace);
throw new Exception("Unexpected error thrown in service when creating a category");
}
}
This is the Guard extension used in the service manager.
public static class GuardExtensions
{
public static void Guard(this string input, string inputName)
{
if (string.IsNullOrEmpty(input))
{
throw new ApplicationException($"{inputName} must be provided");
}
}
public static void Guard(this object input, string inputType)
{
if (input == null)
{
throw new ApplicationException($"{inputType} must be provided");
}
}
}
This is the controller where I'm using the the service manager.
public async Task<IActionResult> Create(CategoryViewModel model)
{
logger.LogInformation("In {controller}, Creating {CategoryViewModel}", nameof(CategoryController), model.ToString());
try
{
if (ModelState.IsValid)
{
int createdCategoryId = await categoryService.CreateCategory(model);
List<CategoryPictureViewModel> categoryPictureViewModels = new List<CategoryPictureViewModel>();
foreach (int picId in TransformTypes.SplitStringIntoListOfInt(model.uploadedImageIds))
{
categoryPictureViewModels.Add(new CategoryPictureViewModel
{
CategoryId = createdCategoryId,
PictureId = picId
});
//model.CategoryPictures.ToList().Add(new CategoryPictureViewModel
//{
// CategoryId = createdCategoryId,
// PictureId = item
//});
}
int res = await categoryPictureService.CreateCategoryPictureAsync(categoryPictureViewModels);
return RedirectToAction(nameof(Index));
}
}
catch (ApplicationException ex)
{
logger.LogError("In {controller}, Creating category: {Message}, {Stacktrace}", nameof(CategoryController), ex.Message, ex.StackTrace);
throw new ApplicationException($"Exception thrown controller when creating category: {ex.Message}");
}
catch (Exception ex)
{
logger.LogError("Unexpected error in {controller} when creating category: {Message}, {Stacktrace}", nameof(CategoryController), ex.Message, ex.StackTrace);
throw new Exception($"Unexpected error in controller when creating category: {ex.Message}");
}
return StatusCode(StatusCodes.Status422UnprocessableEntity);
}
You can handle your exception by Action filter or using a custom exception handling middleware.
It depends on your scenario. but Having a custom exception middleware or exception filter to handle your exception can work and it's better for sake of separation of concern.
using Middleware :
Microsoft
ExceptionFilter:
StackOverflow

Error handling in the repository (API REST)

I have this situation (method in Repository):
public string Get(string name)
{
string response;
try
{
using (var context = new MyDB())
{
var row = context.TblSomething.FirstOrDefault();
response = row.GetType().GetProperty(name).GetValue(row, null).ToString();
}
return response;
}
catch (SqlException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
}
When there is content other than the Property in the name field, it throws an exception
The method is called in the Controller
public IActionResult Get(string name)
{
string response;
try
{
response = _module.MyRepository().Get(name);
}
catch (ValidationException e)
{
return BadRequest(new { error = new { message = e.Message, value = e.Value } });
}
return Ok(response);
}
How to make it not return a 500 error to the user but should be BadRequest?
The way to make it return 400 instead of 500 is to actually catch the exception. You already have a catch block that returns BadRequest, so the only assumption that can be made is that ValidationException is not what's being thrown. Catch the actual exception being thrown and you're good.
That said, absolute do not catch an exception merely to throw the same exception. All you're doing is slowing down your app. You should also never catch Exception, unless you're simply trying to generally log all exceptions and then rethrow. If you don't have a specific handler for an exception type, then don't catch it. In other words, remove these lines:
catch (SqlException e)
{
throw e;
}
catch (Exception e)
{
throw e;
}
If you're not going to handle any exceptions as your repo code does, then don't use a try block at all.
It's also worth mentioning that you shouldn't rely on exceptions unless you have to. Throwing exceptions is a drain on performance. In a situation like this, you should simply return null, instead of throwing an exception when there's no matching property. Then, you can do a null check to verify instead of a try/catch.
You could create your own Exception Handling Middleware to catch 500 error and return your custom error status code and message.
1.Create the middleware:
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
public ExceptionHandlingMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context )
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError;
string message = "Something is wrong!";
httpStatusCode = HttpStatusCode.BadRequest; // Or whatever status code you want to return
message = exception.Message; // Or whatever message you want to return
string result = JsonConvert.SerializeObject(new
{
error = message,
});
context.Response.StatusCode = (int)httpStatusCode;
context.Response.ContentType = "application/json";
return context.Response.WriteAsync(result);
}
}
2.Add it into the middleware pipeline after app.UseDeveloperExceptionPage();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMiddleware(typeof(ExceptionHandlingMiddleware));
}

BusinessRuleException is not working in UI project

I'm working on 3 layer windows application in C#. We have custom exception to capture businessrule exceptions. this is getting captured in Data access layer and business layer. But it is not falling in businessrule exception in UI project.
[Serializable]
public class BusinessRuleException : ApplicationException
{
public BusinessRuleException():base(){}
public BusinessRuleException(string message):base(message){}
public BusinessRuleException(string message, Exception exception):base(message,exception){}
public BusinessRuleException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context):base(info,context){}
}
In Data access layer, below catch block is getting captured.
catch (BusinessRuleException ex)
{
throw ex;
}
In UI, below catch block is not working. it is going to System.Exception catch block
catch(BusinessRuleException ex)
{
m_strErrorMessage = ex.Message;
}
catch(Exception ex)
{
m_strErrorMessage = ex.Message;
}

Assert Method not reached in Try-Catch statement

I am trying to unit test my code to see if I try to view an account's details without a sessionKey, it will throw an exception. The unit test will go and execute the statement in the try-catch and despite knowing an exception will occur, it lists the test as successful even though Assert should be false. Assert is reached in similar functions, but not always. What would be the cause of the problem?
Original Function
/// <summary>
/// Displays the account details to the user
/// </summary>
/// <returns>HttpResponseMessage deserialized into AccountResponses object</returns>
public async Task<AccountResponse> Details()
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("X-Session-Key", sessionKey);
try
{
HttpResponseMessage response = await client.GetAsync(_baseUrl + "/Account/Details");
response.EnsureSuccessStatusCode();
string details = await response.Content.ReadAsStringAsync();
AccountResponse temp = JsonConvert.DeserializeObject<AccountResponse>(details);
return temp
}
catch (HttpRequestException ex)
{
throw ex;
}
}
Working Live Unit Test Function
[TestCategory("Account/Logon")]
[TestMethod]
public void LogOnNegativeBadPasswordTest()
{
try
{
string sessionKey = dmWeb.Account.LogOn(new DMWeb_REST.AccountLogOn { Password = "test#pasasdfasfsword" }).GetAwaiter().GetResult().ToString();
}
catch (HttpRequestException ex)
{
Assert.IsTrue(ex.Message.Contains("400"));
}
}
Not Working Live Unit Test Function
[TestCategory("Account/Details")]
[TestMethod]
public void DisplayDetailsNegativeNoSessionKeyTest()
{
try
{
string details = dmWeb.Account.Details().GetAwaiter().GetResult().ToString();
}
catch (HttpRequestException ex)
{
Assert.IsTrue(ex.Message.Contains("401"));
}
}
The test will be treated as successful as long as no errors are thrown. If no error is being caught in the catch block, this almost definitely means that no error was thrown by the code you're testing.
So place an Assert.Fail() after the statement that's supposed to throw an error:
public void TestMethod()
{
try
{
string details = ThingIAmTesting();
// shouldn't get here
Assert.Fail();
}
catch (Exception ex)
{
Assert.IsTrue(ex.Message.Contains("401");
}
}

A good solution for await in try/catch/finally?

I need to call an async method in a catch block before throwing again the exception (with its stack trace) like this :
try
{
// Do something
}
catch
{
// <- Clean things here with async methods
throw;
}
But unfortunately you can't use await in a catch or finally block. I learned it's because the compiler doesn't have any way to go back in a catch block to execute what is after your await instruction or something like that...
I tried to use Task.Wait() to replace await and I got a deadlock. I searched on the Web how I could avoid this and found this site.
Since I can't change the async methods nor do I know if they use ConfigureAwait(false), I created these methods which take a Func<Task> that starts an async method once we are on a different thread (to avoid a deadlock) and waits for its completion:
public static void AwaitTaskSync(Func<Task> action)
{
Task.Run(async () => await action().ConfigureAwait(false)).Wait();
}
public static TResult AwaitTaskSync<TResult>(Func<Task<TResult>> action)
{
return Task.Run(async () => await action().ConfigureAwait(false)).Result;
}
public static void AwaitSync(Func<IAsyncAction> action)
{
AwaitTaskSync(() => action().AsTask());
}
public static TResult AwaitSync<TResult>(Func<IAsyncOperation<TResult>> action)
{
return AwaitTaskSync(() => action().AsTask());
}
So my questions is: Do you think this code is okay?
Of course, if you have some enhancements or know a better approach, I'm listening! :)
You can move the logic outside of the catch block and rethrow the exception after, if needed, by using ExceptionDispatchInfo.
static async Task f()
{
ExceptionDispatchInfo capturedException = null;
try
{
await TaskThatFails();
}
catch (MyException ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
await ExceptionHandler();
capturedException.Throw();
}
}
This way, when the caller inspects the exception's StackTrace property, it still records where inside TaskThatFails it was thrown.
You should know that since C# 6.0, it's possible to use await in catch and finally blocks, so you could in fact do this:
try
{
// Do something
}
catch (Exception ex)
{
await DoCleanupAsync();
throw;
}
The new C# 6.0 features, including the one I just mentioned are listed here or as a video here.
If you need to use async error handlers, I'd recommend something like this:
Exception exception = null;
try
{
...
}
catch (Exception ex)
{
exception = ex;
}
if (exception != null)
{
...
}
The problem with synchronously blocking on async code (regardless of what thread it's running on) is that you're synchronously blocking. In most scenarios, it's better to use await.
Update: Since you need to rethrow, you can use ExceptionDispatchInfo.
We extracted hvd's great answer to the following reusable utility class in our project:
public static class TryWithAwaitInCatch
{
public static async Task ExecuteAndHandleErrorAsync(Func<Task> actionAsync,
Func<Exception, Task<bool>> errorHandlerAsync)
{
ExceptionDispatchInfo capturedException = null;
try
{
await actionAsync().ConfigureAwait(false);
}
catch (Exception ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
bool needsThrow = await errorHandlerAsync(capturedException.SourceException).ConfigureAwait(false);
if (needsThrow)
{
capturedException.Throw();
}
}
}
}
One would use it as follows:
public async Task OnDoSomething()
{
await TryWithAwaitInCatch.ExecuteAndHandleErrorAsync(
async () => await DoSomethingAsync(),
async (ex) => { await ShowMessageAsync("Error: " + ex.Message); return false; }
);
}
Feel free to improve the naming, we kept it intentionally verbose. Note that there is no need to capture the context inside the wrapper as it is already captured in the call site, hence ConfigureAwait(false).

Categories

Resources