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();