Action in Controller is not catching an exception - c#

I hope someone can explain what is happening.
I created a repository class "InvoicesRepository.cs" that manage all the logic of listing, inserting, updating, deleting, etc. "Invoice.cs" objects. Is cleaner and easier to maintain.
public class InvoicesRepository
{
protected MySQLContext db;
public InvoicesRepository(MySQLContext db)
{
this.db = db;
}
public async void Insert(Invoice obj)
{
this.db.Invoices.Add(obj);
await this.db.SaveChangesAsync();
// performing other tasks...
}
// other useful methods...
}
There is a "InvoicesController.cs" with all the actions that i require. Inside this controller i create a "InvoiceTepository" obj and then use it to save information to the database. And, in every action
public class InvoicesController : Controller
{
private InvoicesRepository invoices;
public InvoicesController()
{
this.invoices = new InvoicesRepository(new MySQLContext());
}
[HttpPost]
public async Task<ActionResult> Upload(Invoice invoice)
{
try
{
this.invoices.Insert(invoice);
}
catch (DbEntityValidationException ex)
{
foreach (var eve in ex.EntityValidationErrors)
{
foreach (var err in eve.ValidationErrors)
{
ModelState.AddModelError(err.PropertyName, err.ErrorMessage);
}
}
}
catch (System.Data.Entity.Infrastructure.DbUpdateException ex)
{
ModelState.AddModelError("", ex.ToString());
}
catch (System.Data.Entity.Core.UpdateException ex)
{
ModelState.AddModelError("", ex.ToString());
}
catch (MySql.Data.MySqlClient.MySqlException ex)
{
ModelState.AddModelError("", ex.ToString());
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.ToString());
}
return View();
}
// other useful action methods...
}
For testing, i'm inserting an "Invoice" object that has a duplicate data (unique column) in the database expecting to throw an exception and then my action catching it and display properly the error in the view but... the exception is "thrown" but is not "catched".
I debugged to see what kind of exceptions are thrown (including their inner exceptions) and added the required "catches" but still the exception is not "catched".
If i change the code of the controller to use the "MySQLContext.cs" class directly to save the info the exception is "catched":
[HttpPost]
public async Task<ActionResult> Upload(Invoice invoice)
{
try
{
// this is "catched" ¿?
this.db.Invoices.Add(obj);
await this.db.SaveChangesAsync();
}
catch (Exception ex)
{
ModelState.AddModelError("", ex.ToString());
}
return View();
}
Why is this happening? I need to be able catch any exception that my "Insert" or any other function in the "InvoiceRepository" class throw inside the controller.
Any help would be appreciated.

You are not awaiting the Insert method so it's not possible for your action to catch any exception. Visual Studio should have given you a warning message that nothing has be awaited in that method. Change your code to this:
await this.invoices.Insert(invoice);
Also, your repository should not use async void, it should return a Task

Related

how to handle an exception "Invalid file signature" in asp.net core?

I am using .txt file instead of using excel file so I should be getting 400 error but I am getting 500 error. I want to catch the exception and send a 400 response code with an appropriate response body.
[Route("file/")]
[AuthorizeFunction(AuthFunctions.Schema.Create)]
[HttpPost]
[ResponseType(typeof(Schema))]
public async Task<IActionResult> Create([FromBody] Guid fileId)
{
var result = await _SchemaService.Create(fileId);
return Created("GetSchema", new { id = result.Id }, result);
}
You can use this code to catch specific error
[Route("file/")]
[AuthorizeFunction(AuthFunctions.Schema.Create)]
[HttpPost]
[ResponseType(typeof(Schema))]
public async Task<IActionResult> Create([FromBody] Guid fileId)
{
try {
var result = await _SchemaService.Create(fileId);
return Created("GetSchema", new { id = result.Id }, result);
}
catch (Exception exc){
if (exc.GetType().FullName == "Your_Exception_Name")
{
// Check your exception name here
}
}
}
or
catch(Exception ex)
{
if(ex.InnerException is ExceptionInstance)// exception instance type you want to check
{
}
}
Update
You can just use catch(Exception ex) for general exception then return BadRequest()
catch(Exception ex)
{
return BadRequest();
}

Handling exceptions from service layer applying clean code

I have a service that saves an employee, if the employee email or code already exists the service return an Exception that is catched in the controller and then add the error to return the view. I'm starting practicing clean code, and I want to know if this implementation is fine or if the service.Save(e) method should validate if email/code exists and just return a status code.
public async Task<ActionResult> Create(Employee e)
{
if (ModelState.IsValid)
{
try
{
service.Save(e);
RedirectToAction("Index");
}
catch (EmailExistException ex)
{
ModelState.AddModelError("email", ex.Message);
}
catch (CodeExistException ex)
{
ModelState.AddModelError("code", ex.Message);
}
}
return View(e);
}

Suppress error from Unit of Work transaction

I'm trying to suppress error in code, but MVC action still returns "500 internal server error".
What events are fired in ASP.NET Boilerplate framework after action returns?
public async Task<IActionResult> Post([FromBody]PaymentViewModel model)
{
var result = false;
// Storing of card must pass
try
{
// ...
}
catch (Exception ex)
{
// Catch business exception, but storing
}
return Json(new { result });
}
To recover in a catch block, begin a UnitOfWork with RequiresNew:
public async Task<IActionResult> Post([FromBody]PaymentViewModel model)
{
var result = false;
// Storing of card must pass
try
{
using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
{
// ...
await CurrentUnitOfWork.SaveChangesAsync();
await uow.CompleteAsync();
}
}
catch (Exception ex)
{
// Catch business exception, but storing
}
return Json(new { result });
}
Further explanation: aspnetboilerplate/aspnetboilerplate#2732 (comment)

How can I throw an exception in an ASP.NET Core WebAPI controller that returns an object?

In Framework WebAPI 2, I have a controller that looks like this:
[Route("create-license/{licenseKey}")]
public async Task<LicenseDetails> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return await _service.DoSomethingAsync(license).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
throw new HttpResponseException(HttpStatusCode.InternalServerError, msg);
}
}
Sure enough, I get back a 500 error with the message.
How can I do something similar in ASP.NET Core Web API?
HttpRequestException doesn't seem to exist. I would prefer to continue returning the object instead of HttpRequestMessage.
What about something like this. Create a middleware where you will expose certain exception messages:
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
public ExceptionMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
if (ex is ApplicationException)
{
await context.Response.WriteAsync(ex.Message);
}
}
}
}
Use it in your app:
app.UseMiddleware<ExceptionMiddleware>();
app.UseMvc();
And then in your action throw the exception:
[Route("create-license/{licenseKey}")]
public async Task<LicenseDetails> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return await _service.DoSomethingAsync(license).ConfigureAwait(false);
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
throw new ApplicationException(msg);
}
}
A better approach is to return an IActionResult. That way you dont have to throw an exception around. Like this:
[Route("create-license/{licenseKey}")]
public async Task<IActionResult> CreateLicenseAsync(string licenseKey, CreateLicenseRequest license)
{
try
{
// ... controller-y stuff
return Ok(await _service.DoSomethingAsync(license).ConfigureAwait(false));
}
catch (Exception e)
{
_logger.Error(e);
const string msg = "Unable to PUT license creation request";
return StatusCode((int)HttpStatusCode.InternalServerError, msg)
}
}
It's better not to catch all exceptions in every action. Just catch exceptions you need to react specifically and catch (and wrap to HttpResponse) all the rest in Middleware.

Handling exception in task

I'm new to TPL.
I need to handle exception when the SendEmailAlert() method throws any error.Is the following code correct please?
public Task MyMethod()
{
DoSomething();
try
{
string emailBody = "TestBody";
string emailSubject = "TestSubject";
Task.Run(()=> SendEmailAlert(arrEmailInfo));
}
catch (AggregateException ex)
{
ex.Handle((e) =>
{
log.Error("Error occured while sending email...", e);
return true;
}
);
}
}
private void SendEmailAlert(string[] arrEmailInfo)
{
MyClassX.SendAlert(arrEmailnfo[0], arrEmailnfo[1]);
}
I forced an error from within SendEmailAlert() method.But the exception is not getting caught. Could someone advise?
Thanks.
Your Task.Run runs in a different context (you would need a try/catch inside it; or check if the task is done). You could change to use async/await.
Example:
public async void MyMethod()
{
try
{
await ExceptionMethod();
}
catch (Exception ex)
{
// got it
}
}
public async Task ExceptionMethod()
{
throw new Exception();
}

Categories