通用存储库,工作单元,统一性的架构问题

时间:2019-01-06 14:49:36

标签: c# entity-framework-6 unity-container

我正在使用MVC5,EntityFramework,Unity,UnitOfWork和通用存储库来开发我的项目体系结构之一,但我还没有使用过AutoMapper或其他类似于AutoMapper的东西。

每当我第一次执行更新时,它都能很好地工作,但是第二次以后,它给我以下错误。

  

附加类型为'EntityModel.tblCompanyMaster'的实体失败,因为相同类型的另一个实体已经具有相同的主键值。如果图形中的任何实体具有相互冲突的键值,则使用“附加”方法或将实体的状态设置为“不变”或“修改”时,可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到数据库生成的键值。在这种情况下,请使用“添加”方法或“已添加”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。

更新方法在此处[_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方面做错了,但是很难确定。预先感谢您的帮助。

3 个答案:

答案 0 :(得分:0)

错误非常明显。以下是来自here的引用:

  

将给定实体附加到集合下的上下文。也就是说,将实体以未更改状态放置到上下文中,就像从数据库中读取它一样。
  .....
  .....
  如果实体已经在上下文中处于“未更改”状态,则附加操作为空操作。

当您第一次致电_dbSet.Attach(entity);时,与您分离的entity成为DbSet的一部分;即它被放置在上下文中。该实体还具有用于标识实体的唯一标识符(主键值)。

现在,当您第二次调用它时,标识符/密钥是相同的。一个标识符DbSet中不能存在两个具有相同标识符的实体。

您应在致电Attach之前检查该实体是否已存在。

在致电_dbSet.Find(key)之前使用Attach。如果已经存在,请不要调用AttachThis可能会有所帮助。


这不是问题的一部分,但是我想您更新记录的方式不正确。如果要更新记录,更好的方法是从数据库中获取(Find / SingleOrDefault)=>修改要更改的属性=>刷新(调用{{1 }}) 变化。请参考this问题。

答案 1 :(得分:0)

  

每当我第一次执行更新时,它都能很好地工作,但是第二次以后,它给我以下错误。

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

答案 2 :(得分:0)

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

CREATE TRIGGER trig_update_sma
AFTER INSERT ON symbol_daily_ohlc
FOR EACH ROW
EXECUTE PROCEDURE update_sma8();