通用存储库、工作单元、Unity 的架构问题

Architectural issue with Generic Repository, Unit Of Work, Unity

我正在使用 MVC5、EntityFramework、Unity、UnitOfWork 和 Generic Repository 开发我的一个项目架构,我也没有使用 AutoMapper 或任何与 AutoMapper 类似的东西。

每当我第一次执行更新时它都能完美运行,但第二次之后它会给我以下错误。

Attaching an entity of type 'EntityModel.tblCompanyMaster' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

此处的更新方法发生错误[_dbSet.Attach(entity)]

 public virtual void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null)
    {
        _dbSet.Attach(entity);
        _dbContext.Entry<TEntity>(entity).State = EntityState.Modified;
      .
      .
     }

这是我的代码:

1) 团结

public static class UnityConfig
{
    public static void RegisterComponents()
    {
        var container = new UnityContainer();
        //container.RegisterType<DbContext, dbTestCMSEntities>();
        container.RegisterSingleton<IUnitOfWork, UnitOfWork>();
        container.RegisterSingleton(typeof(IDbHelper<>), typeof(DbHelper<>));            
        container.RegisterSingleton<ICompanyMasterBL, tblCompanyMasterBL>();            
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
    }
}

2) 工作单元

    public interface IUnitOfWork
{
    dbTestCMSEntities dbContext { get; }
    void Save();
}

public class UnitOfWork : IUnitOfWork, IDisposable
{
    public dbTestCMSEntities dbContext { get; }
    private bool _disposed = false;

    public UnitOfWork(dbTestCMSEntities context)
    {
        dbContext = context;
    }

    public void Save()
    {
        try
        {
            dbContext.SaveChanges();
        }
        catch (Exception ex)
        {
            throw ex;
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this._disposed)
        {
            if (disposing)
            {
                dbContext.Dispose();
            }
        }
        this._disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

3) 通用存储库

public interface IDbHelper<TEntity> where TEntity : class
{
    bool Exists(object pkId);
    TEntity GetFirst(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, dynamic>> order = null);
    IEnumerable<TEntity> GetMany(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, string>> order = null);
    IEnumerable<TEntity> GetPagedList(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, Int64>> _order, int currentPageIndex, out int Total);
    void InsertEntity(TEntity entity);
    void InsertEntity(List<TEntity> entity);
    void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null);
    void UpdateEntity(List<TEntity> entity, string[] NoUpdateProperty = null);
    void Delete(object id);
    void Delete(TEntity entity);
    void DeleteEntity(Expression<Func<TEntity, bool>> condition);
}

public class DbHelper<TEntity> : IDbHelper<TEntity> where TEntity : class
{
    protected readonly dbTestCMSEntities _dbContext;
    protected DbSet<TEntity> _dbSet;
    public DbHelper(IUnitOfWork unitOfWork)
    {
        _dbContext = unitOfWork.dbContext;
        _dbSet = _dbContext.Set<TEntity>();
    }

    public bool Exists(object pkId)
    {
        return _dbSet.Find(pkId) != null;
    }

    public TEntity GetFirst(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, dynamic>> order = null)
    {
        IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
        if (order != null)
            lstResult = lstResult.AsNoTracking().OrderBy(order).AsQueryable();
        if (condition != null)
            lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();

        TEntity entity = lstResult.AsNoTracking().FirstOrDefault();
        return entity;
    }

    public virtual IEnumerable<TEntity> GetMany(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, string>> order = null)
    {
        IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
        if (condition != null)
            lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();
        if (order != null)
            lstResult = lstResult.AsNoTracking().OrderBy(order).AsQueryable();
        return lstResult;
    }

    public virtual IEnumerable<TEntity> GetPagedList(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, Int64>> _order, int currentPageIndex, out int Total)
    {
        IQueryable<TEntity> lstResult = _dbSet.AsNoTracking().AsQueryable();
        if (condition != null)
            lstResult = lstResult.AsNoTracking().Where(condition).AsQueryable();
        Total = lstResult.Count();
        lstResult = lstResult.AsNoTracking().OrderByDescending(_order).AsQueryable().Skip((currentPageIndex - 1) * StringUtility.ItemsPerPage).Take(StringUtility.ItemsPerPage);
        return lstResult;
    }

    /// <summary>
    /// Insert single record
    /// </summary>
    /// <param name="entity"></param>
    public virtual void InsertEntity(TEntity entity)
    {
        _dbSet.Add(entity);
    }

    /// <summary>
    /// Insert multiple records
    /// </summary>
    /// <param name="entity"></param>
    public virtual void InsertEntity(List<TEntity> entity)
    {
        _dbSet.AddRange(entity);
    }

    /// <summary>
    /// Update single entity
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="NoUpdateProperty"></param>
    public virtual void UpdateEntity(TEntity entity, string[] NoUpdateProperty = null)
    {
        _dbSet.Attach(entity);
        _dbContext.Entry<TEntity>(entity).State = EntityState.Modified;
        if (NoUpdateProperty != null)
        {
            foreach (string item in NoUpdateProperty)
            {
                _dbContext.Entry<TEntity>(entity).Property(item).IsModified = false;
            }
        }
    }

    /// <summary>
    /// Update multiple entity
    /// </summary>
    /// <param name="entity"></param>
    /// <param name="NoUpdateProperty"></param>
    public virtual void UpdateEntity(List<TEntity> entity, string[] NoUpdateProperty = null)
    {
        foreach (TEntity item in entity)
        {
            _dbSet.Attach(item);
            _dbContext.Entry<TEntity>(item).State = EntityState.Modified;
            if (NoUpdateProperty != null)
            {
                foreach (string item1 in NoUpdateProperty)
                {
                    _dbContext.Entry<TEntity>(item).Property(item1).IsModified = false;
                }
            }
        }
    }

    /// <summary>
    /// Generic Delete method for the entities. Delete one record only.
    /// </summary>
    /// <param name="id"></param>
    public virtual void Delete(object id)
    {
        TEntity entityToDelete = _dbSet.Find(id);
        Delete(entityToDelete);
    }

    /// <summary>
    /// Generic Delete method for the entities. Delete one record only.
    /// </summary>
    /// <param name="entityToDelete"></param>
    public virtual void Delete(TEntity entity)
    {
        if (_dbContext.Entry(entity).State == EntityState.Detached)
        {
            _dbSet.Attach(entity);
        }
        _dbSet.Remove(entity);
    }

    /// <summary>
    /// Delete one or many records based on given condition
    /// </summary>
    /// <param name="condition"></param>
    public void DeleteEntity(Expression<Func<TEntity, bool>> condition)
    {
        _dbSet.RemoveRange(_dbSet.Where(condition));
    }
}

我尝试了很多方法但没有帮助我,我相信我在 Unity 和 Unit Of Work 上做错了,但很难识别。在此先感谢您的帮助。

Whenever I perform update for first time it works perfectly, but for second time onwards it gives me below error.

可能您正在尝试为多个请求重新使用 DbContext 实例。您的 DI 容器似乎正在将事物注册为单身人士。 DbContext 应该根据请求确定范围。

下面的代码可以帮助我找到我的解决方案,只需在更新时在 Attach() 之前调用此方法即可:

public Boolean Exists(T entity) {
var objContext = ((IObjectContextAdapter)this._dbContext).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);

Object foundEntity;
var exists = objContext.TryGetObjectByKey(entityKey, out foundEntity);

if (exists) {
    objContext.Detach(foundEntity);
}
return (exists);
}