删除实体时实体跟踪器出现问题

问题描述 投票:0回答:1

我正在构建一个电子商务,更确切地说是购物车,并尝试在删除购物车时实现添加/删除项目的会话。我目前正在使用 ASP.NET Core MVC 和 EF Core 7.0.10。我做了一些关于实体跟踪器、如何正确删除、更新项目 ecc 的研究。但尽管如此,我在实体跟踪器方面遇到了这个问题。这是以下错误:

InvalidOperationException:无法跟踪实体类型“ShoppingCart”的实例,因为已跟踪具有相同键值 {'Id'} 的另一个实例。

我读到未跟踪的实体是优化的更好原因。在此代码中,我已经应用了 asNoTracking() 方法,因此不会跟踪它们。下面我展示了控制器的操作方法中使用的存储库代码。

public IEnumerable<T> GetAll(
            Expression<Func<T, bool>>? filter = null,
            string? includeProperties = null
        )
        {

            IQueryable<T> query = _dbSet;
           
            // Here i apply a X filter
            if (filter != null)
            {
                query = query.Where(filter);
            }

            // Here i can populate properties
            if (!string.IsNullOrEmpty(includeProperties))
            {
                foreach (
                    var property in includeProperties
                    .Split(
                        new char[] { ',' },
                        StringSplitOptions.RemoveEmptyEntries)
                    )
                {
                    query = query.Include(property);
                }
            }
            return query.ToList();
        }

GetAll() 方法与 Get() 方法相同(显然也带有 asNoTracking())

我还读到,要删除与内容分离的实体,我必须首先将分离的实体附加到上下文,然后将该实体标记为“已删除”:以下代码:

_dbSet.Attach(entity).State = EntityState.Deleted;

我已经在其他代码上尝试过这个“策略”,它有效,但在这种情况下不起作用。我不明白原因。我尝试使用 _context 而不是使用 _unitOfWork 编写所有操作,并且它有效。怎么可能?使用 _context 和 _unitOfWork 有什么区别?

这是ShoppingCart控制器的操作方法。

public IActionResult Remove(int cartId)
        {

            ShoppingCart? cart = _unitOfWork.ShoppingCartRepo.Get(x => x.Id == cartId);
            if (cart == null) return NotFound("Cart not found in Remove(int cartId)");

            // Before saving i set new value for the session
            HttpContext.Session.SetInt32(
                key: StaticDetails.SessionCart,
                value: _unitOfWork.ShoppingCartRepo.GetAll
                (filter: x => x.ApplicationUserId == cart.ApplicationUserId)
                .Count() - 1
            );

            _unitOfWork.ShoppingCartRepo.Remove(cart);

            _unitOfWork.Save();

            return RedirectToAction(nameof(Index));

        }

这是其存储库中remove()的实现。


public void Remove(T entity, bool entityStateDeleted = true)
        {
            if (entityStateDeleted)
            {
                _dbSet.Attach(entity).State = EntityState.Deleted;
            }
            else
            {
                _dbSet.Remove(entity);
            }
        }

c# asp.net-mvc entity-framework .net-core entity
1个回答
0
投票

GetAll() 方法与 Get() 方法相同(显然也带有 AsNoTracking())

方法

Get
返回一个未被跟踪的对象。如果我重新表述该方法
Remove(int cartId)
,例如:

IActionResult Remove(int cartId) // For the exemple, cartId is 42
{
    ShoppingCart? cart = _dbSet.AsNoTracking().FirstOrDefault(x => x.Id == cartId);
    // The shopping cart 42 is loaded in memomy, but not tracked

    List<ShoppingCart> userShoppingCarts = _dbSet.Where(x => x.ApplicationUserId == cart.ApplicationUserId).ToList();
    // All user shopping cart are loaded in memory with tracking.

    // Then the shoshopping cart 42 is traked, but from a other object than that is referenced by the variable cart
    ShoppingCart cartDuplicated = userShoppingCarts.First(c => c.Id == cart.Id);
    Object.ReferenceEqual(cart, cartDuplicated); // False

    int userShoppingCartsCount = userShoppingCarts.ToList();


    // The error come from :
    _dbSet.Attach(cart)
    // Throw the error :
    // InvalidOperationException: The instance of entity type 'ShoppingCart' cannot be tracked because
    //     another instance with the same key value for {'Id'} is already being tracked.

    // Translated :
    // cart cannot be tracked because cartDuplicated with the id 42 is already being tracked.
    ...
}

解决方案是反转

AsNoTracking
的位置。

AsNoTracking
Get
移至
GetAll
:

public IEnumerable<T> GetAll(...)
{
    IQueryable<T> query = _dbSet.AsNoTacking();
    ...
}

然后您只需删除:

public void Remove(T entity)
{
    _dbSet.Remove(entity);
}

备注:统计使用购物车时,所有用户购物车都会加载到内存中。实在是效率太低了。我建议你做类似的事情:

var count = _dbSet.Where(x => x.ApplicationUserId == cart.ApplicationUserId).Count();

因此,只有计数值被加载到内存中。为此,您可以在存储库中添加

Count
方法。

© www.soinside.com 2019 - 2024. All rights reserved.