user91489
user91489

Reputation: 65

Multiple calls to a controller's function causes problems (EF Core in an ASP.NET MVC web app)

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

Answers (1)

Svyatoslav Danyliv
Svyatoslav Danyliv

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

Related Questions