После написания многих репозиториев и интерфейсов для моего доступа к данным я понял, что я переписывал много кодов снова и снова, поэтому я попытался понять общий шаблон репозитория и блок работы. Я следил за учебником здесь. После внедрения примера и включения необходимой части в мой проект. Я столкнулся с проблемой
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;
Это все еще не сработало. или, может быть, что-то еще проблема.
Из различных сообщений и исследований я наконец узнал, что проблема в том, что я использовал отдельный экземпляр 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;
Надеюсь, это поможет другим людям.
Проблема заключается в этом источнике строки. source.Agent = currentUser.SalesAgent;
Вы устанавливаете объект SalesAgent
который был SalesAgent
одним экземпляром ApplicationDbContext
который отличается от экземпляра, который использовался для получения source
.
UnitOfWork
вместо UserStore.