Handling exceptions from service layer applying clean code - c#

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);
}

Related

Trying to show exception message in UI controller in ASP.NET MVC

In the configuration file I have made it so that the property Name of the class Series IsUnique() so whenever I try to add another entity with the same name, I get a DbUpdateException. I can access the message of this exception everywhere except for the UIController.
Here we have the code in my service where I check if the series is valid and if not I throw an exception (I know this is not best practice put at this point I just want it to work first)
public void Add(SeriesDTO series)
{
if (series.Name != null && series.Startdate < series.Enddate)
{
_unitOfWork.Series.AddAsync(_mapper.Map<SeriesDTO, Series>(series));
_unitOfWork.CommitAsync();
}
else
throw new Exception("Series data is not valid");
}
Then I have my controller where I check for the DbUpdateException and if I find it I throw another exception this I prefer not to do because at this point I can access this exception message with the right message.
[HttpPost("add")]
//POST: series/add
public IActionResult Add(SeriesDTO series)
{
try
{
_seriesService.Add(series);
}
catch (DbUpdateException E)
{
throw new Exception("Series with this name already exists.");
}
return Ok(series);
}
Up until this point I can always access the exception error but when I get to my UI controller then this exception turns into a 500 internal server error and thus I can not differentiate between an invalid entity exception and a DbUpdateException and thus cannot access the right message.
public IActionResult Add(SeriesDTO serie)
{
if(serie.Enddate < DateTime.Today || serie.Enddate.Equals(null))
{
serie.Active = false;
}
else
{
serie.Active = true;
}
try
{
string data = JsonConvert.SerializeObject(serie);
var result = _client.UploadString("series/add", data);
}
catch(Exception E)
{
ExceptionModel Exception = new ExceptionModel("Something went wrong with the series data.");
return View("Exception", Exception);
//return View("Create");
}
return Redirect("Index");
}
Does anyone know how to properly send the exception through to the UI controller?
You can do that.
In you Controller method :
public IActionResult Add(SeriesDTO serie)
{
//...
ModelState.AddModelError("CustomError", "You error message as string.");
//...
}
And in you view you need that :
<span asp-validation-for="CustomError" class="text-danger"></span>
If something doesn't work, tell me and edit you post with the code of you View.

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();
}

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)

Action in Controller is not catching an exception

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

Is it possible to show Error Message in VOID type for webapi?

I am new to Web Api 2 , I want to show some message if i hit Exception , I know that VOID is a non return type, I tried with HttpResponseMessage but it is not working or showing the "Error Message" , is that any way to show message ?
My code is as below
public void Post(int id)
{
string result = string.Empty;
try
{
//some code
}
catch (Exception ex)
{
HttpResponseMessage response2 = Request.CreateResponse(HttpStatusCode.BadRequest, "Error message");
ErrId = 999;
}
}
If you are using web api 2, then I would use IHttpActionResult as the return type, which allows you to do something like:
public IHttpActionResult Post(int id)
{
string result = string.Empty;
try
{
//some code
return Ok();
}
catch (Exception ex)
{
return BadRequest("Error message");
}
}
However, it must be noted that by default Web API has a built-in exception handler and logger that will automatically return a 500 response code for any exception, so the code above is potentially redundant unless you are catching a custom exception. Lastly the code above does not log the exception.

Categories