Home > Software design >  System.InvalidOperationException: The instance of entity type cannot be tracked because another inst
System.InvalidOperationException: The instance of entity type cannot be tracked because another inst

Time:05-31

I have an Api deployed on Azure in which I have a CrCart entity. I want to make sure that if there is already a cart associated with a user and a customer with the status "Saved" you can't add more with that status.

The issue here is that I make that validation on my Post method on my CrCart Service and it works fine but on my Put method it doesn't.

Here is the error:

System.InvalidOperationException: The instance of entity type 'CrCart' cannot be tracked because another instance with the same key value for {'Id'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using...

Here is the code for both my Insert and Update methods:

public async Task Insert(CrCart cart)
        {
            var user = await _unitOfWork.CrUserRepository.GetById(cart.IdUser);
            var customer = await _unitOfWork.CrCustomerRepository.GetById(cart.IdCustomer);

            var carts = _unitOfWork.CrCartRepository.GetAll();
            carts = carts.Where(x => x.IdCustomer == cart.IdCustomer && x.Status == "Saved" && x.IdUser == cart.IdUser);

            if (user == null)
            {
                throw new BusinessException("El usuario introducido no existe");
            }

            if (customer == null)
            {
                throw new BusinessException("El cliente introducido no existe");
            }

            if (carts.Count() >= 1 && cart.Status == "Saved") {

                throw new BusinessException("Ya existe un carrito con estado -Saved- asignado al cliente "  
                    customer.Name);
            }   

            await _unitOfWork.CrCartRepository.Insert(cart);
            await _unitOfWork.SaveChangesAsync();
        }

        public async Task<bool> Update(CrCart cart)
        {
            var user = await _unitOfWork.CrUserRepository.GetById(cart.IdUser);
            var customer = await _unitOfWork.CrCustomerRepository.GetById(cart.IdCustomer);

            var carts = _unitOfWork.CrCartRepository.GetAll();
            carts = carts.Where(x => x.IdCustomer == cart.IdCustomer && x.Status == "Saved" && x.IdUser == cart.IdUser);

            if (user == null)
            {
                throw new BusinessException("El usuario introducido no existe");
            }

            if (customer == null)
            {
                throw new BusinessException("El cliente introducido no existe");
            }

            if (carts.Count() >= 1 && cart.Status == "Saved")
            {

                throw new BusinessException("Ya existe un carrito con estado -Saved- asignado al cliente "  
                    customer.Name);
            }

            _unitOfWork.CrCartRepository.Update(cart);
            await _unitOfWork.SaveChangesAsync();

            return true;

        }

I already have my service Scopped so that's not the issue. Please help because I don't know what it is causing this and I have a deadline for this project. Thanks.

EDIT

My BaseRepository in which I have generic methods

 public class BaseRepository<T> : IBaseRepository<T> where T: BaseEntity
    {
        private readonly db_crijoyaContext _context;
        protected readonly DbSet<T> _entities;

        public BaseRepository(db_crijoyaContext context)
        {
            _context = context;
            _entities = context.Set<T>();
        }

        public IEnumerable<T> GetAll()
        {
           return _entities.AsEnumerable();
        }

        public async Task<T> GetById(int id)
        {
            return await _entities.FindAsync(id);
        }

        public async Task Insert(T entity)
        {
            await _entities.AddAsync(entity);
            
            //await _context.SaveChangesAsync();
        }

        public void Update(T entity)
        {
            
            _entities.Update(entity);
            
            //await _context.SaveChangesAsync();
        }

        public async Task Delete(int id)
        {
            T entity = await GetById(id);
            _entities.Remove(entity);

            //await _context.SaveChangesAsync();

        }

    }

And my UnitOfWork where I have all the repositories

public class UnitOfWork : IUnitOfWork 
    {
        private readonly db_crijoyaContext _context;
        private readonly ICrProductRepository _productRepsitory;
        private readonly IBaseRepository<CrCompany> _companyRepository;
        private readonly IBaseRepository<CrCategory> _categoryRepository;
        private readonly IBaseRepository<CrCart> _cartRepository;
        private readonly IBaseRepository<CrCustomer> _customerRepository;
        private readonly IBaseRepository<CrUser> _userRepository;
        private readonly IBaseRepository<CrOrder> _orderRepository;
        private readonly IBaseRepository<CrCity> _cityRepository;
        private readonly ICrUserInfoRepository _userInfoRepository;
        private readonly ICrCountryRepository _countryRepository;

        public UnitOfWork(db_crijoyaContext context)
        {
            _context = context;
        }

        public ICrProductRepository CrProductRepository => _productRepsitory ?? new CrProductRepository(_context);
        public IBaseRepository<CrCategory> CrCategoryRepository => _categoryRepository ?? new BaseRepository<CrCategory>(_context);
        public IBaseRepository<CrCompany> CrCompanyRepository => _companyRepository ?? new BaseRepository<CrCompany>(_context);
        public IBaseRepository<CrUser> CrUserRepository => _userRepository ?? new BaseRepository<CrUser>(_context);
        public IBaseRepository<CrCart> CrCartRepository => _cartRepository ?? new BaseRepository<CrCart>(_context);
        public IBaseRepository<CrCustomer> CrCustomerRepository => _customerRepository ?? new BaseRepository<CrCustomer>(_context);
        public IBaseRepository<CrOrder> CrOrderRepository => _orderRepository ?? new BaseRepository<CrOrder>(_context);
        public IBaseRepository<CrCity> CrCityRepository => _cityRepository ?? new BaseRepository<CrCity>(_context);
        public ICrUserInfoRepository CrUserInfoRepository => _userInfoRepository ?? new CrUserInfoRepository(_context);
        public ICrCountryRepository CrCountryRepository => _countryRepository ?? new CrCountryRepository(_context);

        public void Dispose()
        {
            if(_context != null)
            {
                _context.Dispose();
            }
        }

        public void SaveChanges()
        {
            _context.SaveChanges();
        }

        public async Task SaveChangesAsync()
        {
            await _context.SaveChangesAsync();
        }

EDIT 2

Here's the working code which I don't know if it is done properly

public async Task<bool> Update(CrCart cart)
        {
            var user = await _unitOfWork.CrUserRepository.GetById(cart.IdUser);
            var customer = await _unitOfWork.CrCustomerRepository.GetById(cart.IdCustomer);

            var carts = _unitOfWork.CrCartRepository.GetAllWithNoTracking();
            carts = carts.Where(x => x.IdCustomer == cart.IdCustomer && x.Status == "Saved" && x.IdUser == cart.IdUser);

            if (user == null)
            {
                throw new BusinessException("El usuario introducido no existe");
            }

            if (customer == null)
            {
                throw new BusinessException("El cliente introducido no existe");
            }

            if (carts.Count() >= 1 && cart.Status == "Saved")
            {

                throw new BusinessException("Ya existe un carrito con estado -Saved- asignado al cliente "  
                    customer.Name);
            }

             _unitOfWork.CrCartRepository.Update(cart);
            await _unitOfWork.SaveChangesAsync();

            return true;

        }

public IEnumerable<T> GetAllWithNoTracking()
        {
            return _entities.AsNoTracking().AsEnumerable();
        }

CodePudding user response:

Sometimes if you use async methods with entities without await or waiting, and before your first async task finished, you started to call another task, it will throw this exception.

So, in addition, you should use AsNoTracking() method with entities you will not change and then will not save (just read).

UPDATE 1: In the end of your code check this lines:

_unitOfWork.CrCartRepository.Update(cart);
await _unitOfWork.SaveChangesAsync(); 

Maybe you need to add await before _unitOfWork.CrCartRepository.Update(cart);

UPDATE 2:

public IEnumerable<T> GetAllWithNoTracking()
{
   return _entities.AsNoTracking().AsEnumerable();
}

public async Task<T?> GetByIdWithNoTracking(int id)
{
    return await _entities.AsNoTracking().FirstOrDefault(x => x.id == id);
}

or you can replace it with AsNoTrackingWithIdentityResolution() if you have newest EF CORE.

UPDATE 3 Also try this:

_context.Entry(entity).State = EntityState.Modified;
_context.SaveChanges();
  • Related