无法跟踪实体类型Model的实例,因为已经跟踪了具有相同键值的{'Id'}的另一个实例

时间:2019-08-11 14:44:03

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

我有问题,什么时候可以在数据库中进行更新,我有这个异常。

  

无法跟踪实体类型“ 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 <ExpenseReport> () 我已经在使用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();
        }

1 个答案:

答案 0 :(得分:2)

您无需致电_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 IEnumberable
    /// </summary>
    /// <returns>An IEnumberable 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();
}

请注意,如果未跟踪实体,则仅需要调用“附加”或“ 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();
}