EF4 更新导航 属性 一个实体对象不能被 IEntityChangeTracker 的多个实例引用
EF4 update navigation property An entity object cannot be referenced by multiple instances of IEntityChangeTracker
我正在尝试实现一个 "change my account email address" 功能。
我想备份 (R_EmailAddressHistory table) 中的所有用户电子邮件。
下面是我项目的一些代码。
public bool ChangeEmailAddress(string username, string newEmailAddress, string callbackUrl)
{
DateTime currentUtcTime = DateTime.UtcNow;
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
if (currentUser.HasPendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
EmailHistoRepo.Update(currentPendingRequest);
}
else
{
currentUser.HasPendingNewEmail = true;
R_EmailAddressHistory newEmail = new R_EmailAddressHistory();
newEmail.UserId = currentUser.GId;
newEmail.R_User = currentUser;
newEmail.NewEmail = newEmailAddress;
newEmail.InsertDate = currentUtcTime;
newEmail.StatusID = (int) Reno.Common.Enums.RecordStatus.Pending;
currentUser.R_EmailAddressHistory.Add(newEmail);
}
IdentityResult idtResult = UserRepo.Update(currentUser);
if(idtResult == IdentityResult.Succeeded)
{
//Send notification to current email address for validation before proceeding change email process
bool sendResult = Communication.EmailService.SendChangeEmailValidation(username,currentUser.Email, newEmailAddress, callbackUrl);
return sendResult;
}
else
{
return false;
}
}
前面的方法是用来更改邮箱地址。我的每个 table(R_User 和 EmailAddressHistory)都有存储库(UserRepo 和 EmailHistoRepo)。实现相同的 IRepositoryBase class,这里是更新方法
public IdentityResult Update(T entity)
{
try
{
if (_currentContext.DbContext.Entry(entity).State == EntityState.Detached)
{
_currentContext.DbContext.Set<T>().Attach(entity);
}
_currentContext.DbContext.Entry(entity).State = EntityState.Modified;
return IdentityResult.Succeeded;
}
catch
{
return IdentityResult.Failed;
}
}
当用户已经有一个未验证的新电子邮件地址时,当他请求更改他当前的电子邮件地址时,我会向他显示待处理的新电子邮件地址,他可以更改它,在这种情况下,我想更新我的历史table 而不是创建一个新的,因为只允许一个待处理的新电子邮件地址。在这种情况下,我的代码在行 EmailHistoRepo.Update(currentPendingRequest) 中失败并抛出错误:实体对象不能被 IEntityChangeTracker 的多个实例引用。
谁能帮帮我?
谢谢
编辑
我正在使用带有 unitOfWork 的 MVC(4)。第一次查询数据库时,我的 UOW 在控制器中初始化,提交在 Appalication_EndRequest 中的 global.asax 文件中完成(见下文)。
protected void Application_EndRequest(Object sender, EventArgs e)
{
CommitChanges();
}
private void CommitChanges()
{
Reno.BLL.Services.Singleton.UnitOfWork unitOfWork = Reno.BLL.Services.Singleton.UnitOfWork.GetCurrentInstance(false);
if (unitOfWork != null)
{
unitOfWork.Commit();
unitOfWork.Dispose();
}
}
您的 currentUser 在更新电子邮件地址之前被修改。首先保存对 currentUser 的更改。
像这样:
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
bool pendingNewEmail = currentUser.HasPendingNewEmail;
UserRepo.Update(currentUser);
if (pendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
EmailHistoRepo.Update(currentPendingRequest);
}
else
我终于找到了答案。问题是当我第一次让用户排队时
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser 变量持有其所有 R_EmailAddressHistory 的引用。
然后,我查询数据库(第二次)以获取待处理的电子邮件更改请求(或键入 R_EmailAddressHistory)以修改其新电子邮件及其更新日期,行
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
但是最后的代码仅更新 currentPendingRequest,而 currentUser.R_EmailAddressHistory 中同一对象的另一个引用未更新并且已被上下文跟踪。因此,通过更新新实例 (EmailHistoRepo.Update(currentPendingRequest)),代码失败:如果在 2 个地方引用相同的对象。
所以,解决方案是(我唯一修改的地方):
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
if (currentUser.HasPendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = currentUser.R_EmailAddressHistory.Where(h => h.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending).First(); // EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
}
我决定修改 currentUser 变量中的实例。
我正在尝试实现一个 "change my account email address" 功能。
我想备份 (R_EmailAddressHistory table) 中的所有用户电子邮件。
下面是我项目的一些代码。
public bool ChangeEmailAddress(string username, string newEmailAddress, string callbackUrl)
{
DateTime currentUtcTime = DateTime.UtcNow;
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
if (currentUser.HasPendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
EmailHistoRepo.Update(currentPendingRequest);
}
else
{
currentUser.HasPendingNewEmail = true;
R_EmailAddressHistory newEmail = new R_EmailAddressHistory();
newEmail.UserId = currentUser.GId;
newEmail.R_User = currentUser;
newEmail.NewEmail = newEmailAddress;
newEmail.InsertDate = currentUtcTime;
newEmail.StatusID = (int) Reno.Common.Enums.RecordStatus.Pending;
currentUser.R_EmailAddressHistory.Add(newEmail);
}
IdentityResult idtResult = UserRepo.Update(currentUser);
if(idtResult == IdentityResult.Succeeded)
{
//Send notification to current email address for validation before proceeding change email process
bool sendResult = Communication.EmailService.SendChangeEmailValidation(username,currentUser.Email, newEmailAddress, callbackUrl);
return sendResult;
}
else
{
return false;
}
}
前面的方法是用来更改邮箱地址。我的每个 table(R_User 和 EmailAddressHistory)都有存储库(UserRepo 和 EmailHistoRepo)。实现相同的 IRepositoryBase class,这里是更新方法
public IdentityResult Update(T entity)
{
try
{
if (_currentContext.DbContext.Entry(entity).State == EntityState.Detached)
{
_currentContext.DbContext.Set<T>().Attach(entity);
}
_currentContext.DbContext.Entry(entity).State = EntityState.Modified;
return IdentityResult.Succeeded;
}
catch
{
return IdentityResult.Failed;
}
}
当用户已经有一个未验证的新电子邮件地址时,当他请求更改他当前的电子邮件地址时,我会向他显示待处理的新电子邮件地址,他可以更改它,在这种情况下,我想更新我的历史table 而不是创建一个新的,因为只允许一个待处理的新电子邮件地址。在这种情况下,我的代码在行 EmailHistoRepo.Update(currentPendingRequest) 中失败并抛出错误:实体对象不能被 IEntityChangeTracker 的多个实例引用。
谁能帮帮我? 谢谢
编辑
我正在使用带有 unitOfWork 的 MVC(4)。第一次查询数据库时,我的 UOW 在控制器中初始化,提交在 Appalication_EndRequest 中的 global.asax 文件中完成(见下文)。
protected void Application_EndRequest(Object sender, EventArgs e)
{
CommitChanges();
}
private void CommitChanges()
{
Reno.BLL.Services.Singleton.UnitOfWork unitOfWork = Reno.BLL.Services.Singleton.UnitOfWork.GetCurrentInstance(false);
if (unitOfWork != null)
{
unitOfWork.Commit();
unitOfWork.Dispose();
}
}
您的 currentUser 在更新电子邮件地址之前被修改。首先保存对 currentUser 的更改。
像这样:
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
bool pendingNewEmail = currentUser.HasPendingNewEmail;
UserRepo.Update(currentUser);
if (pendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
EmailHistoRepo.Update(currentPendingRequest);
}
else
我终于找到了答案。问题是当我第一次让用户排队时
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser 变量持有其所有 R_EmailAddressHistory 的引用。 然后,我查询数据库(第二次)以获取待处理的电子邮件更改请求(或键入 R_EmailAddressHistory)以修改其新电子邮件及其更新日期,行
R_EmailAddressHistory currentPendingRequest = EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
但是最后的代码仅更新 currentPendingRequest,而 currentUser.R_EmailAddressHistory 中同一对象的另一个引用未更新并且已被上下文跟踪。因此,通过更新新实例 (EmailHistoRepo.Update(currentPendingRequest)),代码失败:如果在 2 个地方引用相同的对象。
所以,解决方案是(我唯一修改的地方):
R_User currentUser = UserRepo.GetSingle(whereCondition: w=>w.Username == username);
currentUser.UpdateDate = currentUtcTime;
if (currentUser.HasPendingNewEmail)
{
R_EmailAddressHistory currentPendingRequest = currentUser.R_EmailAddressHistory.Where(h => h.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending).First(); // EmailHistoRepo.GetSingle(whereCondition: w => w.StatusID == (int)Reno.Common.Enums.RecordStatus.Pending && w.R_User.GId == currentUser.GId);
currentPendingRequest.NewEmail = newEmailAddress;
currentPendingRequest.UpdateDate = currentUtcTime;
}
我决定修改 currentUser 变量中的实例。