I am using ASP.NET Boilerplate with Code-First Entity Framework and MVC 5.
I want to handle concurrency. In the Update method, I put Timestamp data annotation for RowVersion field in my entity.
In the manager of my entity and in Update operation, I am trying to catch DbUpdateConcurrencyException exception and throw UserFriendlyException but because UpdateAsync is asynchronous, I don't know where to handle the exception.
Where should I handle this exception to be user-friendly and not to see Internal Server Error?
public abstract class BaseFullAuditedEntity : FullAuditedEntity<Guid>
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class Branch : BaseFullAuditedEntity
{
public string Name { get; set; }
}
Manager code:
public interface IBranchManager : IDomainService
{
Task<Branch> Update(Branch branch, byte[] RowVersion);
}
public class BranchManager : DomainService, IBranchManager
{
private IRepository<Branch, Guid> _branchRepository { get; }
public async Task<Branch> Update(Branch branch, byte[] RowVersion)
{
try
{
return await _branchRepository.UpdateAsync(branch);
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
}
UpdateAsync(branch) only adds branch to the context.
Inject IUnitOfWorkManager and await SaveChangesAsync():
try
{
await _branchRepository.UpdateAsync(branch);
await _unitOfWorkManager.Current.SaveChangesAsync(); // Add this
return branch;
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
Alternatively, override SaveChanges and SaveChangesAsync in your DbContext to catch for all entities:
public override int SaveChanges()
{
try
{
return base.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
public override async Task<int> SaveChangesAsync()
{
try
{
return await base.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
throw new UserFriendlyException("Update Concurrency Happened");
}
}
Try catching as an aggregate exception...
(Sometimes, the real exception is being wrapped.)
try
{
// code...
}
catch (Exception ex)
{
if (ex is AggregateException)
{
var exMsg = FlattenAggregate((AggregateException)ex);
throw new UserFriendlyException(exMsg );
}
throw;
}
public static string FlattenAggregate(AggregateException aggregateException)
{
var sbErr = new StringBuilder();
var exceptions = aggregateException.Flatten();
foreach (var exception in exceptions.InnerExceptions)
{
sbErr.AppendLine(exception.ToString());
}
return sbErr.ToString();
}
Related
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
I am a beginner in AWS and at present, I am sending data from DyanmoDB leveraging on TTL to AWS Lambda and then to an End Point. My manager wants me to handle a situation where Lambda throws some exception thus fails to deliver the events to an endpoint.
In case of exception from Lambda he wants me to send the entry back to the DynamoDB table. I am sure it should be doable by using Put-Item command. But I want to know is there any out of the box solution that Lambda provides with which I can handle the failure condition and reprocess that received event data, and thus not lose the entries from DyanmoDB Stream during an exception. By doing this I won't have to send the data back to DynamoDB.
Below is working code for AWS Lambda
public class Function
{
private JsonSerializer _jsonSerializer = new JsonSerializer();
private readonly IQueueClient client;
public async Task FunctionHandler(DynamoDBEvent dynamoEvent, ILambdaContext context)
{
try
{
foreach (var record in dynamoEvent.Records)
{
try
{
if (record.EventName == OperationType.REMOVE)
{
context.Logger.LogLine("Calling SerializeStreamRecord function");
string streamRecordJson = SerializeStreamRecord(record.Dynamodb);
Debug.Write(streamRecordJson);
await SendAsync(streamRecordJson, context);
context.Logger.LogLine("Data Sent");
}
}
catch (Exception ex)
{
throw ex;
}
}
}
catch (Exception ex)
{
context.Logger.LogLine("Exception Occurred" + ex.Message);
}
context.Logger.LogLine("Stream processing complete.");
}
private async Task SendAsync(string stream, ILambdaContext context)
{
try
{
var message = new Message(Encoding.UTF8.GetBytes(stream));
await client.SendAsync(message); // SEND MESSAGE
}
catch (Exception ex)
{
throw ex;
}
}
private string SerializeStreamRecord(StreamRecord streamRecord)
{
try
{
using (var writer = new StringWriter())
{
_jsonSerializer.Serialize(writer, streamRecord);
return writer.ToString();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
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));
}
I wonder what I am doing wrong in the following implementation.
I cannot able to see loading dialog, even to opening the ClassroomViewModel takes few seconds.
public IMvxCommand ClassroomSelectedCommand => new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelected);
private async Task ClassroomSelected(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}
You are using async APIs, use an MvxAsynCommand
private IMvxAsynCommand _classroomSelectedCommand;
public IMvxAsynCommand ClassroomSelectedCommand => _classroomSelectedCommand ?? (_classroomSelectedCommand = new MvxAsyncCommand<ClassroomViewModel>(ClassroomSelectedAsync));
private async Task ClassroomSelectedAsync(Model obj)
{
using (UserDialogs.Instance.Loading("Loading..."))
{
await Task.Delay(300);
try
{
ShowViewModel<ClassroomViewModel>(new { Id = obj.Id });
}
catch (Exception ex)
{
}
}
}
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)
...