What does it mean by catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))? - c#

What does the below code block mean:
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
Full sample:
// PUT: api/TodoItems/5
// To protect from overposting attacks, see https://go.microsoft.com/fwlink/?linkid=2123754
[HttpPut("{id}")]
public async Task<IActionResult> UpdateTodoItem(long id, TodoItemDTO todoItemDTO)
{
if (id != todoItemDTO.Id)
{
return BadRequest();
}
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
return NotFound();
}
todoItem.Name = todoItemDTO.Name;
todoItem.IsComplete = todoItemDTO.IsComplete;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException) when (!TodoItemExists(id))
{
return NotFound();
}
return NoContent();
}

This is called an exception filter clause. It is normally used like this:
// without exception filters:
try
{
var file = new StreamReader(myInputStream);
// ....
}
catch (IOException x)
{
// Handle error
}
catch (UnauthorizedAccessException x)
{
// Handle error (same as above)
}
catch (SocketException x)
{
// Handle error (again, same as above)
}
// etc., etc...
// Instead, one can write
try
{
var file = new StreamReader(myInputStream);
// ....
}
catch (Exception x) when (x is SocketException || x is UnauthorizedAccessException || x is IOException)
{
// Handle all expected exception types in one handler
}
I have never seen it being used as in your example. And I'm not sure it is used correctly. What it does is that it only enters the catch clause when TodoItemsExists returns false. That means, on the other hand, that if a DbUpdateConcurrencyException is thrown and TodoItemsExists returns true, the catch handler is not invoked and the exception falls trough, eventually crashing the server task.
I'm not sure about the actual requirement, but I think the following is intented instead:
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TodoItemExists(id))
{
return NotFound();
}
}
return NoContent();
So this will return the appropriate error message to the caller (either NotFound() or NoContent()) when the exception is thrown.
The condition in the when clause can be used to do an additional test whether the handler should be invoked.

Related

Unit Test C# Can we test catch block?

let's say i have a method that logins in;
public async Task<IActionResult> Login()
{
try
{
//my codes..
}
catch(Exception exp)
{
_sLogger.Slack(exp)
return BadRequest();
}
}
i have tested all controllers and methods but i can't test the catch block? i can't throw a exception, how can i do that?
i have tried like this but that doesn't work for me:
public void_WithInvalidData_ThenBadRequest()
{
authController.UnAuthrorize();
var result=(BadRequestResult)authController.Login();
Assert.True(result.StatusCode == (int)HttpStatusCode.BadRequest);
authController.Authorize();
}
Use following code
public async Task<IActionResult> Login()
{
try
{
if (conditions) throw new Exception($"Message"); // throw an exception
}
catch(Exception exp)
{
_sLogger.Slack(exp)
return BadRequest();
}
}

Why is the else part of my if/then statement not being used?

I have an Entity Framework Core project with this HttpPut method inside of my controllers.
I have an if/else statement that checks two booleans, isValid and isDeleted.
In testing, when I send over an object to the controller with isValid set to true and isDeleted set to false, it send the correct email.
But if I send over an object with isValid set to true and isDeleted set to true, it doesn't send out an email.
Is my code logic out of hand crazy?
thanks!
[HttpPut("{id}")]
public async Task<IActionResult> PutShoppingCart(long id, [FromBody] ShoppingCart shoppingCart)
{
_context.Entry(shoppingCart).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ShoppingCartExists(id))
{
return NotFound();
}
else
{
throw;
}
}
if (shoppingCart.IsValid && !shoppingCart.IsDeleted)
{
try
{
_customerEmailerProcess.SendCustomerEmail(shoppingCart,10);
}
catch (Exception ex)
{
await HttpResponseWritingExtensions.WriteAsync(this.Response, "<script>alert('" + HttpUtility.HtmlEncode(ex) + "')</script>");
}
} else
{
if (shoppingCart.IsDeleted)
{
try
{
_customerEmailerProcess.SendCustomerEmail(shoppingCart, 20);
}
catch (Exception ex)
{
await HttpResponseWritingExtensions.WriteAsync(this.Response, "<script>alert('" + HttpUtility.HtmlEncode(ex) + "')</script>");
}
}
}
return NoContent();
}
It looks like your if/else statement should work, so my guess is that there is a problem in the SendCustomerEmail method when the second parameter is 20. Does the method expect the shoppingCart object to have IsValid set to true?
The easiest thing to do to solve it is run it through the debugger and step through it with the shoppingCart.IsDeleted property set to false.

API only returns responses with no content

My methods only return responses with no content.
Controller
[HttpGet("Floors/{floorId}", Name = "FloorById")]
public IActionResult GetFloor(int floorId)
{
try
{
Floor floor = _repository.Floor.GetFloor(floorId);
if (floor == null)
return NotFound();
return Ok(floor);
}
catch (Exception e)
{
return StatusCode(500, "text");
}
}
Repository
public Floor GetFloor(int floorId)
{
return _context.Floors.FirstOrDefault(f => f.Id == floorId);
}
Ideally, this code should return an Ok response with the object as well.
Instead, I only get an Ok response when using swagger. Not even the NotFound.
Swagger is unable to determine what type the action returns based on the IActionResult.
Use the ProducesResponseType attribute:
[ProducesResponseType(typeof(Floor), 200)] // <-- THIS
[HttpGet("Floors/{floorId}", Name = "FloorById")]
public IActionResult GetFloor(int floorId) {
try {
Floor floor = _repository.Floor.GetFloor(floorId);
if (floor == null)
return NotFound();
return Ok(floor);
} catch (Exception e) {
return StatusCode(500, "text");
}
}

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

C# - Method does not contain a definition for an object

I have this block of code:
[HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromBody] MonitorsDeleteRequest request)
{
if (request == null)
{
return BadRequest("Request could not be parsed.");
}
if (request.MonitorId == Guid.Empty)
{
return BadRequest("Query Monitor Id is required.");
}
try
{
await monitoringService.RemoveMonitorAsync(
new RemoveMonitorRequest()
{
MonitorId = new MonitorId(request.MonitorId)
});
return Accepted();
}
catch (Exception ex)
{
logger.LogError($"[{Request.Path.Value}]: {ex.ToString()}");
return StatusCode(500, ex.Message);
}
}
The MonitorId that is inside the RemoveMonitorRequest method (on the left of the equal sign) is underlined in red - Intellisense says "RemoveMonitorRequest does not contain a definition for MonitorId."
Just FYI, the other MonitorId earlier in the code has no Intellisense error.
What can I do to remedy this?

Categories

Resources