簡體   English   中英

通用存儲庫、工作單元、Unity 的架構問題

[英]Architectural issue with Generic Repository, Unit Of Work, Unity

我正在使用 MVC5、EntityFramework、Unity、UnitOfWork 和 Generic Repository 處理我的項目架構之一,我還沒有使用 AutoMapper 或任何其他類似於 AutoMapper 的東西。

每當我第一次執行更新時,它都能完美運行,但第二次之后,它會出現以下錯誤。

附加類型為“EntityModel.tblCompanyMaster”的實體失敗,因為同一類型的另一個實體已具有相同的主鍵值。 如果圖中的任何實體具有沖突的鍵值,則在使用“Attach”方法或將實體狀態設置為“Unchanged”或“Modified”時可能會發生這種情況。 這可能是因為某些實體是新的,尚未收到數據庫生成的鍵值。 在這種情況下,使用“添加”方法或“添加”實體狀態來跟蹤圖形,然后根據需要將非新實體的狀態設置為“未更改”或“已修改”。

此處的更新方法中發生錯誤 [_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 上做錯了,但很難識別。 提前感謝幫助我。

錯誤非常明顯。 以下是這里的報價:

將給定實體附加到集合的基礎上下文中。 也就是說,將實體以未更改狀態放置到上下文中,就像已從數據庫中讀取實體一樣。
.....
.....
如果實體已經處於Unchanged狀態的上下文中,則Attach為空。

當您調用_dbSet.Attach(entity); 第一次,您的分離entity成為DbSet一部分; 即它被放置在上下文中。 該實體還具有用於標識實體的唯一標識符(主鍵值)。

現在,當您第二次調用它時,標識符/密鑰是相同的。 一個DbSet不能存在具有相同標識符的兩個實體。

您應在調用Attach之前檢查該實體是否已存在。

在調用Attach之前使用_dbSet.Find(key) 如果已經存在,請不要調用Attach 可能會有所幫助。


這不是問題的一部分,但是我想您更新記錄的方式不正確。 如果要更新記錄,更好的方法是從數據庫中獲取( Find / SingleOrDefault )=>修改要更改的屬性=>刷新(調用SaveChanges )更改。 請參考這個問題。

每當我第一次執行更新時,它都可以正常工作,但是第二次之后,它會給我以下錯誤。

可能您正在嘗試將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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM