Search code examples
asp.net-mvcentity-frameworkasp.net-coreentity-framework-corenpgsql

How to remove items without round trip


Code

  var items = await ctx.Cartitems.Where((c) => c.Cartid == GetCartId() && c.Toodeid == product).ToListAsync();
  ctx.Cartitems.RemoveRange(items);
  await ctx.SaveChangesAsync();

Removes product from shopping cart in EF Core. It isseus to commands to database: SELECT and DELETE.

How to remove items from database so that only single command is sent to server ?

ASP.NET 5 MVC Core application using EF Core with Npgsql


Solution

  • If you have the CartItemIds, or the IDs that make up a composite PK, then you can short-cut the delete operations by attaching a stub cart ID and marking it for deletion. Depending on the lifetime scope of your DbContext and where this call sits, you many need to check each one against the local DbContext cache before attaching.

    So, assuming your UI can pass a collection of CartItemIds to flush:

    // This looks for any locally loaded cart items. We will remove these rather than
    // attaching stubs which would raise an exception. This doesn't hit the DB.
    var localCachedCartItems = _context.CartItems.Local
        .Where(x => cartItemIds.Contains(x.CartItemId))
        .ToList();
    // Get the IDs to exclude from the Id list to create stubs.
    var localCachedCartItemIds = localCachedCartItems
        .Select(x => x.CartItemId)
        .ToList();
    // Build stubs for the remaining cart items.
    var cartItemsToRemove = cartItemIds.Except(localCachedCartItemIds)
        .Select(id => new CartItem { CartItemId = id })
        .ToList();
    
    foreach(var stub in cartItemsToRemve)
    {
        _context.Attach(stub);
        _context.Entity(stub).State = EntityState.Deleted;
    }
    
    _context.SaveChanges();
    

    If you are using locally scoped DbContexts (I.e. /w using blocks) then you can skip the Local check and just create the stubs:

    using(var context = new AppDbContext())
    {
        var cartItemsToRemove = cartItemIds
            .Select(id => new CartItem { CartItemId = id })
            .ToList();
    
        foreach(var stub in cartItemsToRemve)
        {
            _context.Attach(stub);
            _context.Entity(stub).State = EntityState.Deleted;
        }
    
        context.SaveChanges();
    }