Asp.Net Web Api Odata V4 - Concurrency Checking - c#

Below code is generated from Wep Api Odata v4 scaffolding tool.
PUT Method
public IHttpActionResult Put([FromODataUri] string key, Delta<Product> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Product product = db.Products.Find(key);
if (product == null)
{
return NotFound();
}
patch.Put(product);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
PATCH Method:
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] string key, Delta<Product> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Product product = db.Products.Find(key);
if (product == null)
{
return NotFound();
}
patch.Patch(product);
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(product);
}
Rowversion field in model
[Timestamp]
public byte[] RowVersion { get; set; }
Questions:
I need to implement concurrency check. How can I check optimistic
concurrency in a Odata way (Using Etag)?
In above code DbUpdateConcurrencyExceptionnever thrown. Any
reason?
There is a attribute called [ConcurrencyCheck] what is the use
of it? Can I use this?
Providing a code sample will be highly appreciated!.

i solve this checking my self the concurrency fields because the patch or put dont does.
This is my code where plato.TimeStamp is the [Timestamp] property.
public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<Plato> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Plato plato = await db.Platos.FindAsync(key);
if (plato == null)
{
return NotFound();
}
//Here save the current value in the DB
string timeStamp = Convert.ToBase64String(plato.TimeStamp);
patch.Put(plato);
try
{
//Here plato.TimeStamp is update from remote, must be equal to stored value
if (timeStamp != Convert.ToBase64String(plato.TimeStamp))
{
throw new DbUpdateConcurrencyException();
}
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PlatoExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(plato);
}

Related

Testing crud operations using xunit in an asp.net web api core application [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am completely new to unit testing and will like to write unit test for my controller
below using xunit and moq. I understand that you can only mock database transactions and
make the unit test independent of the database.
How can I go about writing xunit tests for these? any documentation or sample code will be appreciated.
I know how to add the xnit test project to my solution.
using System;
using System.Threading.Tasks;
using CoreServices.Models;
using CoreServices.Repository;
using Microsoft.AspNetCore.Mvc;
namespace CoreServices.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class PostController : ControllerBase
{
IPostRepository postRepository;
public PostController(IPostRepository _postRepository)
{
postRepository = _postRepository;
}
[HttpGet]
[Route("GetCategories")]
public async Task<IActionResult> GetCategories()
{
try
{
var categories = await postRepository.GetCategories();
if (categories == null)
{
return NotFound();
}
return Ok(categories);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPosts")]
public async Task<IActionResult> GetPosts()
{
try
{
var posts = await postRepository.GetPosts();
if (posts == null)
{
return NotFound();
}
return Ok(posts);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpGet]
[Route("GetPost")]
public async Task<IActionResult> GetPost(int? postId)
{
if (postId == null)
{
return BadRequest();
}
try
{
var post = await postRepository.GetPost(postId);
if (post == null)
{
return NotFound();
}
return Ok(post);
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("AddPost")]
public async Task<IActionResult> AddPost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
var postId = await postRepository.AddPost(model);
if (postId > 0)
{
return Ok(postId);
}
else
{
return NotFound();
}
}
catch (Exception)
{
return BadRequest();
}
}
return BadRequest();
}
[HttpPost]
[Route("DeletePost")]
public async Task<IActionResult> DeletePost(int? postId)
{
int result = 0;
if (postId == null)
{
return BadRequest();
}
try
{
result = await postRepository.DeletePost(postId);
if (result == 0)
{
return NotFound();
}
return Ok();
}
catch (Exception)
{
return BadRequest();
}
}
[HttpPost]
[Route("UpdatePost")]
public async Task<IActionResult> UpdatePost([FromBody]Post model)
{
if (ModelState.IsValid)
{
try
{
await postRepository.UpdatePost(model);
return Ok();
}
catch (Exception ex)
{
if (ex.GetType().FullName == "Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException")
{
return NotFound();
}
return BadRequest();
}
}
return BadRequest();
}
}
}
You can just instantiate that controller with new PostController(postRepository).
If you need a mock for the post repository you can use a library called Moq. (Use nuget to get it). Simply write:
using Moq;
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
then you can use the Setup method on the Mock to return what you want. For example to Mock the Get method in a way that returns a test Post for any given Id of type int?:
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
Then you can execute the code to get the result from the controller, for example (Test Code is made using NUnit, I don't use xunit but the concept is the same):
using Moq;
[TestFixture]
public class ExampleFixture()
{
[Test]
public void ExampleTest()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object })
var result = controller.GetPost(1);
Assert.That(result, Is.Not.Null, "Unexpected null result");
var retrievedPostContent = result as OkNegotiatedContentResult<Post>;
Assert.That(retrievedPostContent, Is.Not.Null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.That(retrievedPost.Id, Is.EqualTo(1), "retrievedPost.Id is unexpected")
}
}
with xUnit should became:
using Xunit;
using Moq;
namespace ATestNamespace
{
public class TestFixture
{
public TestFixture()
{
}
[Fact]
public void Test()
{
Mock<IPostRepository> postRepositoryMock = new Mock<IPostRepository>();
PostController controller = new PostController(postRepositoryMock.Object);
postRepositoryMock.Setup(it => it.Get(It.IsAny<int?>()).Returns(new Post() { //here what you need to build your post object });
var result = controller.GetPost(1);
Assert.True(result != null,"Unexpected null result");
var retrievedPostResult = result as OkNegotiatedContentResult<Post>;
Assert.True(retrievedPostResult != null, "Unexpected null retrievedPost");
var retrievedPost = result.Content;
Assert.True(retrievedPost.Id == 1, "retrievedPost.Id is unexpected")
}
}
}

Issue creating a generic try catch task method

I am trying to convert my TryCatch exception handling used in multiple places to a generic method.
Aim here is for the custom method to take service method as parameter and return the result as Customer object.
I am using async Task so have written a basic ErrorHandling method as such
public Task<T> ErrorHandlingWrapper<T>(Func<Task<T>> action)
{
try
{
return action();
//return Ok(action());
}
catch (Exception ex)
{
throw new Exception(ex.Message);
//return StatusCode(500, e.Message);
}
}
In the above method I want to replicate what my original async Task does as shown below:
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
Models.Customer customer;
try
{
customer = await _service.GetCustomer(custId);
if(customer == null)
{
return BadRequest("Error retrieving customer");
}
}
catch (Exception e)
{
return StatusCode(500, e.Message);
}
return Ok(note);
}
After modifying my original method to use the new error handling method
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
return ErrorHandlingWrapper<Models.Customer>(async () =>
await _service.GetCustomer(custId)
);
}
With the above code am getting an exception
Cannot implicitly convert type System.Threading.Tasks.Task To return type IActionResult?
I am not really sure if my approach is correct?
Update
Customer.cs
public class Customer{
public int CustId { get; set; }
public string FirtName { get; set; }
public string LastName { get; set; }
}
The problem is with some wrong return types and the missing use of async/await. I refactored your example to return IActionResult. This should do:
public async Task<IActionResult> ErrorHandlingWrapper<T>(Func<Task<T>> action)
{
try
{
//return action();
return Ok(await action());
}
catch (Exception ex)
{
//throw new Exception(ex.Message);
return StatusCode(500, ex.Message);
}
}
[HttpPost("{custId}")]
[Authorize]
public async Task<IActionResult> GetCustomer(int custId)
{
return await ErrorHandlingWrapper(async () =>
await _service.GetCustomer(custId)
);
}

In an AspNetCore API controller, is there a well-accepted way to DRY out repetitious validation code?

The context: An AspNetCore controller I've been asked to maintain contains methods similar to the following:
// Get api/Foo/ABCXXX/item/12345
[HttpGet("{accountId}/item/{itemNumber}")]
public async Task<ActionResult<ItemViewModel>> GetFoo([FromRoute] string accountId, [FromRoute] int itemNumber)
{
if (string.IsNullOrWhiteSpace(accountId))
{
return BadRequest("accountId must be provided");
}
if (itemNumber < 0)
{
return BadRequest("itemNumber must be positive");
}
if (!await CanAccessAccountAsync(accountId))
{
return Forbid();
}
// Returns null if account or item not found
var result = _fooService.GetItem(accountId, itemNumber);
if (result == null)
{
return NotFound();
}
return result;
}
// GET api/Foo/ABCXXX
[HttpGet("{accountId}")]
public async Task<ActionResult<IEnumerable<ItemViewModel>>> GetFoos([FromRoute] string accountId)
{
if (string.IsNullOrWhiteSpace(accountId))
{
return BadRequest("accountId must be provided");
}
if (!await CanAccessAccountAsync(accountId))
{
return Forbid();
}
// Returns null if account not found
var results = _fooService.GetItems(accountId);
if (results == null)
{
return NotFound();
}
return Ok(results);
}
You may assume that there are more than 2 such methods with very similar parts.
Looking at this code makes me itchy—there appears to be a lot of repetition, but the repeated parts can't be extracted to their own methods because they contain return statements.
To me, it would make sense for these early exits to be exceptions rather than return values. Say, for the sake of argument, that I define an exception to wrap an IActionResult:
internal class ActionResultException : Exception
{
public ActionResultException(IActionResult actionResult)
{
ActionResult = actionResult;
}
public IActionResult ActionResult { get; }
}
Then I can extract some specific validations:
private void CheckAccountId(string accountId)
{
if (string.IsNullOrWhiteSpace(accountId))
{
throw new ActionResultException(BadRequest("accountId must be provided"));
}
}
private async Task CheckAccountIdAccessAsync(string accountId)
{
if (!await CanAccessAccountAsync(accountId))
{
throw new ActionResultException(Forbid());
}
}
private void CheckItemNumber(int itemNumber)
{
if (itemNumber < 0)
{
throw new ActionResultException(BadRequest("itemNumber must be positive"));
}
}
And rewrite the controller to use them:
// Get api/Foo/ABCXXX/item/12345
[HttpGet("{accountId}/item/{itemNumber}")]
public async Task<IActionResult> GetFoo([FromRoute] string accountId, [FromRoute] int itemNumber)
{
try
{
CheckAccountId(accountId);
CheckItemNumber(itemNumber);
await CheckAccountIdAccessAsync(accountId);
// Returns null if account or item not found
var result = _fooService.GetItem(accountId, itemNumber);
if (result == null)
{
return NotFound();
}
return Ok(result);
}
catch (ActionResultException e)
{
return e.ActionResult;
}
}
// GET api/Foo/ABCXXX
[HttpGet("{accountId}")]
public async Task<IActionResult> GetFoos([FromRoute] string accountId)
{
try
{
CheckAccountId(accountId);
await CheckAccountIdAccessAsync(accountId);
// Returns null if account not found
var results = _fooService.GetItems(accountId);
if (results == null)
{
return NotFound();
}
return Ok(results);
}
catch (ActionResultException e)
{
return e.ActionResult;
}
}
To get this to work, I had to wrap the controller bodies in a try to unwrap the action result from the exception.
I also had to revert the return types to IActionResult—there are reasons I may prefer not to do that. The only thing I can think of to address that problem is to go more specific with the exceptions and catches, but this seems only to shift the WET-ness from the validation code to the catch blocks.
// Exceptions
internal class AccessDeniedException : Exception { ... }
internal class BadParameterException : Exception { ... }
// Controller
private void CheckAccountId(string accountId)
{
if (string.IsNullOrWhiteSpace(accountId))
{
throw new BadParameterException("accountId must be provided");
}
}
private async Task CheckAccountIdAccessAsync(string accountId)
{
if (!await CanAccessAccountAsync(accountId))
{
throw new AccessDeniedException();
}
}
private void CheckItemNumber(int itemNumber)
{
if (itemNumber < 0)
{
throw new BadParameterException("itemNumber must be positive");
}
}
// Get api/Foo/ABCXXX/item/12345
[HttpGet("{accountId}/item/{itemNumber}")]
public async Task<IActionResult> GetFoo([FromRoute] string accountId, [FromRoute] int itemNumber)
{
try
{
...
}
catch (AccessDeniedException)
{
return Forbid();
}
catch(BadParameterException e)
{
return BadRequest(e.Message);
}
}
// GET api/Foo/ABCXXX
[HttpGet("{accountId}")]
public async Task<IActionResult> GetFoos([FromRoute] string accountId)
{
try
{
...
}
catch (AccessDeniedException)
{
return Forbid();
}
catch (BadParameterException e)
{
return BadRequest(e.Message);
}
}
There's a few simple things you can do. No need to go overboard at this point.
First and foremost, checking whether accountId is null or whitespace is completely superfluous. It's part of the route; if something isn't stuck in there, you wouldn't get here in the first place.
Second, you can make judicious use of route constraints where appropriate. For example, for your itemNumber being positive:
[HttpGet("{accountId}/item/{itemNumber:min(0)}")]
Though, honestly, I'm not sure something like /XXXX/item/-1 would even work in the first place. Regardless, specifying a min value will cover you.
Third, your CanAccessAccount check should actually be handled via resource-based authorization, built-in to ASP.NET Core.
Long and short, if you use what's already available to you, you actually don't need to do much additional validation, in the first place, negating the need to find some way to "factor it out".

Update a many-to-many relation in Entity Framework Core

I am trying to update a many-to-many relation in a ASP.NET Core MVC controller using Entity Framework Core. I managed to get this working for adding to the relation, but not updating (leads to a duplicate key error, if I just open/save the entity).
How can I remove the relations from the database before updating/inserting new relations in an efficient way?
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,SalesClerkIds")] Plant plant)
{
if (id != plant.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
plant.SalesClerks = new List<PlantSalesClerk>();
if (plant.SalesClerkIds != null)
{
foreach (var scId in plant.SalesClerkIds)
{
plant.SalesClerks.Add(new PlantSalesClerk()
{
Plant = plant,
User = _context.Users.FirstOrDefault(u => u.Id == scId)
});
}
}
_context.Update(plant);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!PlantExists(plant.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(plant);
}
Write your Edit post method as follows:
public async Task<IActionResult> Edit(int id, [Bind("Id,Name,SalesClerkIds")] Plant plant)
{
if (id != plant.Id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
Plant plantToBeUpdated = await _context.Plants.Include(p => p.SalesClerks).FirstOrDefaultAsync(p => p.Id == id);
if (plantToBeUpdated != null)
{
plantToBeUpdated.SalesClerks.Clear(); // Here you have to clear the existing children before adding the new
if (plant.SalesClerkIds.Count > 0)
{
foreach (var scId in plant.SalesClerkIds)
{
plantToBeUpdated.SalesClerks.Add(new PlantSalesClerk()
{
PlantId = plantToBeUpdated.Id,
UserId = scId
});
}
}
plantToBeUpdated.Name = plant.Name;
// Map other properties here if any
_context.Plants.Update(plantToBeUpdated);
await _context.SaveChangesAsync();
}
}
catch (DbUpdateConcurrencyException)
{
if (!PlantExists(plant.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToAction(nameof(Index));
}
return View(plant);
}
Note: I didn't see your model classes and edit view. I have assumed everything based on your code. So there may needs to be some adjustment but this is the concept of updating model with children in EF core.

Cannot find HTTP resource on OData REST API

Hi I have an OData API that I'm trying to run locally but whenever it is being run I cannot load any of the resources. When I run the API, I get this screen with the information on the entities.
enter image description here
Whenever I try to navigate to one using a URL such as http://localhost:51578/Managers, I get an error saying No HTTP resource was found that matches the request URI 'http://localhost:51578/Managers'. Here is the code I have for the Manager Controller.
public class ManagerController : ODataController
{
DatabaseContext db = new DatabaseContext();
private bool ManagerExists(Guid key)
{
return db.Managers.Any(p => p.Id == key);
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
[EnableQuery]
public SingleResult<Manager> Get([FromODataUri] Guid key)
{
IQueryable<Manager> result = db.Managers.Where(p => p.Id == key);
return SingleResult.Create(result);
}
[HttpPost]
public async Task<IHttpActionResult> Post(Manager manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Managers.Add(manager);
await db.SaveChangesAsync();
return Created(manager);
}
[HttpPatch]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<Manager> manager)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await db.Managers.FindAsync(key);
if (entity == null)
{
return NotFound();
}
manager.Patch(entity);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
[HttpPut]
public async Task<IHttpActionResult> Put([FromODataUri] Guid key, Manager update)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != update.Id)
{
return BadRequest();
}
db.Entry(update).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!ManagerExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(update);
}
[HttpDelete]
public async Task<IHttpActionResult> Delete([FromODataUri] Guid key)
{
var manager = await db.Managers.FindAsync(key);
if (manager == null)
{
return NotFound();
}
db.Managers.Remove(manager);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
}
Make sure that your WebApiConfig.cs file has the following:
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: null,
model: builder.GetEdmModel(),
defaultHandler: HttpClientFactory.CreatePipeline(innerHandler: new HttpControllerDispatcher(config),
handlers: new[] { new ODataNullValueMessageHandler() }));
This will allow your route to be http://localhost:51578/Managers since we set "routePrefix" to null. If route prefix had something else like "v4" your url would look like http://localhost:51578/v4/Managers.

Categories