Reputation: 65
I am using EF Core in an ASP.NET MVC web app.
It is an online restaurant. I want to let the user to change the quantity of a cart item (only adding more in this point) and by that to update his cartItemPrice
and also his cartTotalPrice
(a cart can contain few cart items and every cart item can contain few dishes).
After testing the code, it is working fine, only if I call AddOne
method one at the time, the cartItemPrice
and the CartTotalPrice
are both update as should be.
The problem starts when I call the method many times (by clicking fast.. double clicking for instance causing the problem).
The problem causes the cartItemPrice
to change sometimes, and sometimes not while the CartTotalPrice
changes always.
I used this tag for calling the AddOne
method from the View/Carts :
<div class="quantity">
<a asp-action="AddOne" asp-controller="Carts" asp-route-id="@ci.Id" class="add quantity-btn col-3">+</a>
</div>
I tried to use async and await and also without them and both ways gave the same bad result.
The functions from the Controllers/CartsController:
async/await:
public async Task AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = await _context.Cart.FirstOrDefaultAsync(s => s.UserName == user);
cart.CartTotalPrice += price;
await _context.SaveChangesAsync();
}
public async Task AddOne(int id)
{
var cartitem = await _context.CartItem.Include(d => d.Dish).FirstOrDefaultAsync(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
await AddToTotalPrice(cartitem.Dish.Price);
await _context.SaveChangesAsync();
}
}
Without async/await:
public void AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = _context.Cart.FirstOrDefault(s => s.UserName == user);
cart.cartTotalPrice += price;
_context.SaveChanges();
}
public void AddOne(int id)
{
var cartitem = _context.CartItem.Include(d => d.Dish).FirstOrDefault(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
AddToTotalPrice(cartitem.Dish.Price);
_context.SaveChanges();
}
}
What is wrong here and if it possible to fix?
Thanks.
Upvotes: 0
Views: 775
Reputation: 27471
While server processes one request another request is doing the same thing and you have data race.
You can solve this by two ways:
Transaction
public async Task AddToTotalPrice(double price)
{
var user = GetCurrentUserName();
var cart = await _context.Cart.FirstOrDefaultAsync(s => s.UserName == user);
cart.CartTotalPrice += price;
}
public async Task AddOne(int id)
{
using var tran = _context.Database.BeginTransactionAsync();
var cartitem = await _context.CartItem.Include(d => d.Dish).FirstOrDefaultAsync(s => s.Id == id);
var u = GetCurrentUserName();
var c = _context.Cart.FirstOrDefault(p => p.UserName == u);
if (cartitem != null)
{
if (cartitem.CartId != c.Id)
{
return;
}
cartitem.Quantity += 1;
cartitem.cartItemPrice = cartitem.Dish.Price * cartitem.Quantity;
await AddToTotalPrice(cartitem.Dish.Price);
await _context.SaveChangesAsync();
}
await tran.CommitAsync();
}
Concurrency Token
Add to Cart.CartTotalPrice
attribute [ConcurrencyCheck]
as specified in documentation.
And catch DbUpdateConcurrencyException
and restart updating price again. Handling Concurrency Conflicts
Upvotes: 1