Почему созданный мною репозиторий единиц работы вызывает исключение System.InvalidOperationException при вставке нового элемента

1

После написания многих репозиториев и интерфейсов для моего доступа к данным я понял, что я переписывал много кодов снова и снова, поэтому я попытался понять общий шаблон репозитория и блок работы. Я следил за учебником здесь. После внедрения примера и включения необходимой части в мой проект. Я столкнулся с проблемой

An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
Additional information: An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

Из моих исследований я понял, что могу использовать два контекста базы данных, следовательно, ошибку. У меня есть этот класс GenericRepository и Unit of work class, где я регистрирую все мои репозитории ниже

public class GenericRepository<TEntity> where TEntity : class
{
    internal ApplicationDbContext context;
    internal DbSet<TEntity> dbSet;
    public GenericRepository(ApplicationDbContext context)
    {
        this.context = context;
        this.dbSet = context.Set<TEntity>();
    }
    public virtual IEnumerable<TEntity> Get(
        Expression<Func<TEntity, bool>> filter = null,
        Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
        string includeProperties = "")
    {
        IQueryable<TEntity> query = dbSet;
        if (filter != null)
        {
            query = query.Where(filter);
        }
        foreach (var includeProperty in includeProperties.Split(new char[]{','}, StringSplitOptions.RemoveEmptyEntries))
        {
            query = query.Include(includeProperty);
        }
        if (orderBy != null)
        {
            return orderBy(query).ToList();
        }
        else
        {
            return query.ToList();
        }
    }
    public virtual TEntity GetByID(object id)
    {
        return dbSet.Find(id);
    }
    public virtual void Insert(TEntity entity)
    {
        if (dbSet != null) dbSet.Add(entity);
    }
    public virtual void Delete(object id)
    {
        TEntity entityToDelete = dbSet.Find(id);
        Delete(entityToDelete);
    }
    public virtual void Delete(TEntity entityToDelete)
    {
        if (context.Entry(entityToDelete).State == EntityState.Detached)
        {
            dbSet.Attach(entityToDelete);
        }
        dbSet.Remove(entityToDelete);
    }
    public virtual void Update(TEntity entityToUpdate)
    {
        dbSet.Attach(entityToUpdate);
        context.Entry(entityToUpdate).State = EntityState.Modified;
    }
}

Вот единица рабочего класса

public class UnitOfWork : IDisposable
{
    private ApplicationDbContext context = new ApplicationDbContext();
    private GenericRepository<Transactions> transactionRepository;
    private GenericRepository<PendingReason> penReasonRepository;
    private GenericRepository<DeclineReason> decReasonRepository;
    private GenericRepository<LoanStatus> loanStatusRepository;
    private GenericRepository<SalesAgent> salesAgentRepository;
   public GenericRepository<Transactions> TransactionRepository
    {
        get
        {
            if (this.transactionRepository == null)
            {
                this.transactionRepository = new GenericRepository<Transactions>(context);
            }
            return transactionRepository;
        }
    }
    public GenericRepository<PendingReason> PenReasonRepository
    {
        get
        {
            if (this.penReasonRepository == null)
            {
                this.penReasonRepository = new GenericRepository<PendingReason>(context);
            }
            return penReasonRepository ;
        }
    }
    public GenericRepository<DeclineReason> DecReasonRepository
    {
        get
        {
            if (this.decReasonRepository == null)
            {
                this.decReasonRepository = new GenericRepository<DeclineReason>(context);
            }
            return decReasonRepository;
        }
    }
    public GenericRepository<LoanStatus> LoanStatusRepository
    {
        get
        {
            if (this.loanStatusRepository == null)
            {
                this.loanStatusRepository = new GenericRepository<LoanStatus>(context);
            }
            return loanStatusRepository;
        }
    }
    public GenericRepository<SalesAgent> SalesAgentRepository
    {
        get
        {
            if (this.salesAgentRepository == null)
            {
                this.salesAgentRepository = new GenericRepository<SalesAgent>(context);
            }
            return salesAgentRepository;
        }
    }
    public void Save()
    {
        context.SaveChanges();
    }
    private bool disposed = false;
    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                context.Dispose();
            }
        }
        this.disposed = true;
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

В моем создании действия моего контроллера у меня есть точка, где я использую объект usermanager для получения идентификатора текущего пользователя. В этом пункте есть ссылка на ApplicationDbContext, который, я думаю, вызывает проблему. Однако я могу ошибаться. Ниже приведено действие моего контроллера

    [HttpPost]
    public ActionResult Create(Transactions transactions)
    {
        using (var unit = new UnitOfWork())
        {
            if (ModelState.IsValid)
            {
                MapTransactions(new CreateTrnVM(), transactions);
                unit.TransactionRepository.Insert(transactions);
                unit.Save();
                return RedirectToAction("Index");
            }
            ViewBag.DeclineReasonId = new SelectList(unit.DecReasonRepository.Get(), "DeclineReasonId", "DecReason");
            ViewBag.PendingReasonsId = new SelectList(unit.PenReasonRepository.Get(), "PendingReasonId", "PenReason");
            ViewBag.StatusId = new SelectList(unit.LoanStatusRepository.Get(), "StatusId", "Status");
            return View(new CreateTrnVM());
        }

Это метод MapTransactions.

public void MapTransactions(CreateTrnVM model, Transactions source)
    {
        source.TrnDate = DateTime.Now;
        ApplicationUser currentUser;
        using (var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
            currentUser = manager.FindById(User.Identity.GetUserId());
        }
        source.Agent = currentUser.SalesAgent;
    }

При попытке создать транзакцию эта ошибка продолжает выходить

System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code
    Additional information: An entity object cannot be referenced by multiple instances of IEntityChangeTracker. 

Дальнейшие исследования привели меня к этому заявлению по этой ссылке.

В реальном приложении вам придется решить, хотите ли вы объединить свой контекст данных с IdentityDbContext. Одна из проблем, о которых следует помнить, заключается в том, что класс UserStore не очень хорошо работает при использовании шаблона проектирования единицы измерения. В частности, UserStore вызывает SaveChanges почти по каждому вызову метода по умолчанию, что упрощает преждевременное выполнение единицы работы. Чтобы изменить это поведение, измените флаг AutoSaveChanges в UserStore.

var store = new UserStore<ApplicationUser>(new ApplicationDbContext());
store.AutoSaveChanges = false;

Это все еще не сработало. или, может быть, что-то еще проблема.

  • 0
    В методе MapTransactions вы используете другой DbContext. Это может вызвать проблему. Попробуйте как-то переписать его, чтобы он использовал ваш UnitOfWork вместо UserStore.
  • 0
    Ург ЭФ. я замечаю, что ваши MapTransactions используют БД вне единицы работы. поэтому SalesAgent из UserManager помещается в транзакцию, которая сохраняется модулем. Попробуйте передать модуль в MapTransactions и использовать его для получения currentUser
Показать ещё 6 комментариев
Теги:
entity-framework
unit-of-work
asp.net-mvc-4

2 ответа

1
Лучший ответ

Из различных сообщений и исследований я наконец узнал, что проблема в том, что я использовал отдельный экземпляр ApplicationDbContext, поскольку он столкнулся с тем, который был создан в Unit of work class. Это произошло в том месте, где я пытался получить идентификатор пользователя текущего пользователя в журнале, используя класс UserManager. Небольшое исследование показало мне это из блога K Scot Allen

В реальном приложении вам придется решить, хотите ли вы объединить свой контекст данных с IdentityDbContext. Одна из проблем, о которых следует помнить, заключается в том, что класс UserStore не очень хорошо работает при использовании шаблона проектирования единицы измерения. В частности, UserStore вызывает SaveChanges почти по каждому вызову метода по умолчанию, что упрощает преждевременное выполнение единицы работы. Чтобы изменить это поведение, измените флаг AutoSaveChanges в UserStore.

var store = new UserStore<ApplicationUser>(new ApplicationDbContext());
store.AutoSaveChanges = false;

Предложение выше не помогло мне, но это заставило меня узнать больше о проблеме, и я добрался до этой qaru.site/questions/1508782/... которая предложила создать класс UserManager в группе рабочего класса. Изменение моего кода выше, чтобы включить

private UserManager<ApplicationUser> _userManager;
public UserManager<ApplicationUser> UserManager
{
    get
    {

        if (this._userManager == null)
        {
            this._userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
        }
        return _userManager;
    }
}

Мне удалось получить доступ к классу UserManager в контроллере без непосредственного использования ApplicationDbContext. Я изменил свой контроллер таким образом

public ActionResult Create(Transactions transactions)
        {
            using (var unit = new UnitOfWork())
            {
                if (ModelState.IsValid)
                {
                    transactions.TrnDate = DateTime.Now;
                    var manager = unit.UserManager;
                    var currentUser = manager.FindById(User.Identity.GetUserId());
                    transactions.Agent = currentUser.SalesAgent;
                    unit.TransactionRepository.Insert(transactions);
                    unit.Save();
                    return RedirectToAction("Index");
                }
                ViewBag.StatusId = new SelectList(unit.LoanStatusRepository.Get(), "StatusId", "Status");
                return View(new CreateTrnVM());
            }

Обратите внимание на эти три строки

var manager = unit.UserManager;
var currentUser = manager.FindById(User.Identity.GetUserId());
transactions.Agent = currentUser.SalesAgent;

Надеюсь, это поможет другим людям.

0

Проблема заключается в этом источнике строки. source.Agent = currentUser.SalesAgent;

Вы устанавливаете объект SalesAgent который был SalesAgent одним экземпляром ApplicationDbContext который отличается от экземпляра, который использовался для получения source.

  • 0
    Вы можете помочь с кодом?
  • 0
    Позвольте UserManager использовать IdentityFramework и создайте свой собственный репозиторий для пользователя. Это должно решить вашу проблему

Ещё вопросы

Сообщество Overcoder
Наверх
Меню