【问题标题】:The instance of entity type Model cannot be tracked because another instance with the same key value for {'Id'} is already being tracked无法跟踪实体类型 Model 的实例,因为已经在跟踪具有相同键值 {'Id'} 的另一个实例
【发布时间】:2019-08-11 14:44:03
【问题描述】:

我有一个问题,我什么时候更新我的数据库,我有这个异常。

无法跟踪实体类型“ExpenseReport”的实例,因为 {'Id'} 具有相同键值的另一个实例已经存在 跟踪。附加现有实体时,确保只有一个实体 附加了具有给定键值的实例。考虑使用 'DbContextOptionsBuilder.EnableSensitiveDataLogging' 查看 键值冲突。已经跟踪了

这是我进行更新的方法。

      public async Task UpdateExpenseReportForm(Guid ExpenseReportId)
        {
            var totalValue =   _uow.GetReadRepository<ExpenseItem>().FindByCondition(x => x.ExpenseReportId.Equals(ExpenseReportId)).Sum(x => x.Value);

            var expenseReprot = await _uow.GetReadRepository<ExpenseReport>().FindByCondition(x => x.Id.Equals(ExpenseReportId)).FirstOrDefaultAsync().ConfigureAwait(false);
            expenseReprot.TotalValue = totalValue - expenseReprot.AdvanceValue;
            _uow.GetWriteRepository<ExpenseReport>().Update(expenseReprot);
            await _uow.CommitAsync();

        }

一个重要的细节是在这个方法中_uow.GetReadRepository &lt;ExpenseReport&gt; () 我已经在使用 AsNoTracking 来不映射它

这些是获取和更新"repository dynamic"的方法

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

 public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
        {
            return _dbSet.Where(expression).AsNoTracking();
        }

【问题讨论】:

    标签: c# asp.net sql-server entity-framework


    【解决方案1】:

    您无需致电_dbSet.Update,因为错误消息表明该实体已在您之前的查询中被跟踪。

    尝试从FindByCondition 方法中删除语句“AsNoTracking”,然后在“Update”方法中调用 save:

    public void Update(T entity)
    {
        _dbContext.SaveChanges();
    }
    
    public IQueryable<T> FindByCondition(Expression<Func<T, bool>> expression)
    {
        return _dbSet.Where(expression);
    }
    

    这是一个很好的存储库模式的通用实现,您可能希望重用:

    public class GenericRepository<TEntity> : IRepository<TEntity> where TEntity : class
    {
        /// <summary>
        /// The context object for the database
        /// </summary>
        private DbContext _context;
    
        /// <summary>
        /// The IObjectSet that represents the current entity.
        /// </summary>
        private DbSet<TEntity> _dbSet;
    
        /// <summary>
        /// Initializes a new instance of the GenericRepository class
        /// </summary>
        /// <param name="context">The Entity Framework ObjectContext</param>
        public GenericRepository(DbContext context)
        {
            _context = context;
            _dbSet = _context.Set<TEntity>();
        }
    
        /// <summary>
        /// Gets all records as an IQueryable
        /// </summary>
        /// <returns>An IQueryable object containing the results of the query</returns>
        public IQueryable<TEntity> GetQuery()
        {
            return _dbSet;
        }
    
        /// <summary>
        /// Gets all records as an IQueryable and disables entity tracking
        /// </summary>
        /// <returns>An IQueryable object containing the results of the query</returns>
        public IQueryable<TEntity> AsNoTracking()
        {
            return _dbSet.AsNoTracking<TEntity>();
        }
    
        /// <summary>
        /// Gets all records as an IEnumerable
        /// </summary>
        /// <returns>An IEnumerable object containing the results of the query</returns>
        public IEnumerable<TEntity> GetAll()
        {
            return GetQuery().AsEnumerable();
        }
    
        /// <summary>
        /// Finds a record with the specified criteria
        /// </summary>
        /// <param name="predicate">Criteria to match on</param>
        /// <returns>A collection containing the results of the query</returns>
        public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.Where<TEntity>(predicate);
        }
    
        public Task<TEntity> FindAsync(params object[] keyValues)
        {
            return _dbSet.FindAsync(keyValues);
        }
    
        /// <summary>
        /// Gets a single record by the specified criteria (usually the unique identifier)
        /// </summary>
        /// <param name="predicate">Criteria to match on</param>
        /// <returns>A single record that matches the specified criteria</returns>
        public TEntity Single(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.Single<TEntity>(predicate);
        }
    
        /// <summary>
        /// The first record matching the specified criteria
        /// </summary>
        /// <param name="predicate">Criteria to match on</param>
        /// <returns>A single record containing the first record matching the specified criteria</returns>
        public TEntity First(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.First<TEntity>(predicate);
        }
    
        /// <summary>
        /// The first record matching the specified criteria or null if not found
        /// </summary>
        /// <param name="predicate">Criteria to match on</param>
        /// <returns>A single record containing the first record matching the specified criteria or a null object if nothing was found</returns>
        public TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
        {
            return _dbSet.FirstOrDefault<TEntity>(predicate);
        }
    
        /// <summary>
        /// Deletes the specified entitiy
        /// </summary>
        /// <param name="entity">Entity to delete</param>
        /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
        public void Delete(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            _dbSet.Remove(entity);
        }
    
        /// <summary>
        /// Adds the specified entity
        /// </summary>
        /// <param name="entity">Entity to add</param>
        /// <exception cref="ArgumentNullException"> if <paramref name="entity"/> is null</exception>
        public void Add(TEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentNullException("entity");
            }
            _dbSet.Add(entity);
        }
    
    
        /// <summary>
        /// Attaches the specified entity
        /// </summary>
        /// <param name="entity">Entity to attach</param>
        public void Attach(TEntity entity)
        {
            _dbSet.Attach(entity);
        }
    
        /// <summary>
        /// Detaches the specified entity
        /// </summary>
        /// <param name="entity">Entity to attach</param>
        public void Detach(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Detached;
        }
    
        public void MarkModified(TEntity entity)
        {
            _context.Entry(entity).State = EntityState.Modified;
        }
    
        public DbEntityEntry<TEntity> GetEntry(TEntity entity)
        {
            return _context.Entry(entity);
        }
    
        /// <summary>
        /// Saves all context changes
        /// </summary>
        public void SaveChanges()
        {
            _context.SaveChanges();
        }
    
        /// <summary>
        /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        /// <summary>
        /// Releases all resources used by the WarrantManagement.DataExtract.Dal.ReportDataBase
        /// </summary>
        /// <param name="disposing">A boolean value indicating whether or not to dispose managed resources</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_context != null)
                {
    
                    _context.Dispose();
    
                    _context = null;
    
                }
            }
        }
    }
    

    界面如下:

    public interface IRepository<TEntity> : IDisposable where TEntity : class
    {
        IQueryable<TEntity> GetQuery();
        IEnumerable<TEntity> GetAll();
        IQueryable<TEntity> AsNoTracking();
        IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate);
        TEntity Single(Expression<Func<TEntity, bool>> predicate);
        TEntity First(Expression<Func<TEntity, bool>> predicate);
        TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
        void Add(TEntity entity);
        void Delete(TEntity entity);
        void Attach(TEntity entity);
        void Detach(TEntity entity);
        void MarkModified(TEntity entity);
        void SaveChanges();
    }
    

    注意,如果实体没有被跟踪,只需要调用“Attach”或“MarkModified”,在大多数情况下,你可以简单地进行查询,修改被跟踪实体的一些属性,然后调用SaveChanges

    您还可以将存储库与一个工作单元结合起来,这样您就可以更好地控制事务等...这里是一个示例:

    public class UnitOfWork : IUnitOfWork
    {
        private readonly YouDatabaseContext _context = new YouDatabaseContext();
        private DbContextTransaction _dbContextTransaction;
        private GenericRepository<ExpenseReport> _expenseReportRepository;
        private GenericRepository<ExpenseItem> _expenseItemRepository;
    
        public GenericRepository<ExpenseReport> ExpenseReportRepository
        {
            get
            {
                if (_expenseReportRepository == null)
                {
                    _expenseReportRepository = new GenericRepository<ExpenseReport>(_context);
                }
                return _expenseReportRepository;
            }
    
            set
            {
                _expenseReportRepository = value;
            }
        }
        
        public GenericRepository<ExpenseItem> ExpenseItemRepository
        {
            get
            {
                if (_expenseItemRepository == null)
                {
                    _expenseItemRepository = new GenericRepository<ExpenseItem>(_context);
                }
                return _expenseItemRepository;
            }
    
            set
            {
                _expenseItemRepository = value;
            }
        }
    
        public void BeginTransaction()
        {
            _dbContextTransaction = _context.Database.BeginTransaction();
        }
    
        public void BeginTransaction(IsolationLevel isolationLevel)
        {
            _dbContextTransaction = _context.Database.BeginTransaction(isolationLevel);
        }
    
        public int Save()
        {
            return _context.SaveChanges();
        }
    
        public Task<int> SaveAsync()
        {
            return _context.SaveChangesAsync();
        }
    
        public void Commit()
        {
            if (_dbContextTransaction!=null)
            {
                _dbContextTransaction.Commit();
            }
        }
    
        public void RollBack()
        {
            if (_dbContextTransaction != null)
            {
                _dbContextTransaction.Rollback();
            }
        }
    
        private bool _disposed;
    
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                    _dbContextTransaction?.Dispose();
                }
            }
            _disposed = true;
        }
    
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
    

    还有界面:

    public interface IUnitOfWork : IDisposable
    {
        void BeginTransaction();
        void BeginTransaction(IsolationLevel isolationLevel);
        int Save();
    }
    

    【讨论】:

    • 完美运行。但我不明白为什么 (AsNoTracking) 不起作用。它不会使这种映射发生吗?
    • 应该可以的,也许你之前有一些其他查询或一些外键?我也有与我添加的存储库一起使用的工作单元实现,如果您有兴趣,我也可以发布它。
    • 是的。我有兴趣
    • 我将其添加到问题中。谢谢!编码愉快。
    猜你喜欢
    • 2021-11-24
    • 2020-10-16
    • 2018-12-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-02
    • 2023-01-03
    • 2020-01-31
    相关资源
    最近更新 更多