Архитектурная проблема с общим репозиторием, единицей работы, Unity - PullRequest
0 голосов
/ 06 января 2019

Я работаю над одной из моих архитектурных проектов, используя MVC5, EntityFramework, Unity, UnitOfWork и Generic Repository, также я не использовал 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 голосов
/ 07 января 2019

Всякий раз, когда я выполняю обновление в первый раз, оно работает отлично, но во второй раз это выдает мне ошибку ниже.

Возможно, вы пытаетесь повторно использовать экземпляр DbContext для нескольких запросов. Ваш DI-контейнер, кажется, регистрирует вещи как синглтоны. DbContext должен определяться областью для запроса.

0 голосов
/ 12 января 2019

Приведенный ниже код помогает мне найти решение, просто вызовите этот метод перед 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);
}
0 голосов
/ 07 января 2019

Ошибка очень ясна. Ниже приводится цитата из здесь :

Присоединяет данную сущность к контексту, лежащему в основе набора. Таким образом, объект помещается в контекст в неизмененном состоянии, как если бы он был прочитан из базы данных.
.....
.....
Присоединение не допускается, если объект уже находится в контексте в неизмененном состоянии.

Когда вы звоните _dbSet.Attach(entity); в первый раз, ваши отдельные entity становятся частью DbSet; то есть он помещен в контекст. Этот объект также имеет уникальный идентификатор (значение первичного ключа), который используется для идентификации объекта.

Теперь, когда вы вызываете его во второй раз, идентификатор / ключ тот же. Два объекта с одинаковым идентификатором не могут существовать в одном DbSet.

Перед вызовом Attach следует проверить, существует ли сущность.

Используйте _dbSet.Find(key) перед вызовом Attach. Если уже существует, не звоните Attach. Это может быть полезным.


Не часть вашего вопроса, но я думаю, что вы обновляете запись не правильно. Если вы хотите обновить запись, лучше получить ее (Find / SingleOrDefault) из базы данных => изменить свойство (свойства), которое вы хотите изменить => сбросить (вызвать SaveChanges) изменения. Пожалуйста, обратитесь к этому вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...