简而言之,异常是在POSTing包装器模型期间抛出的,并将一个条目的状态更改为“已修改”。在更改状态之前,状态被设置为“Detached”,但调用Attach()确实会抛出相同的错误。我使用的是EF6。
请在下面找到我的代码(模型名称已更改,以便于阅读)
模型
// Wrapper classes
public class AViewModel
{
public A a { get; set; }
public List<B> b { get; set; }
public C c { get; set; }
}
控制器
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
if (!canUserAccessA(id.Value))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
var aViewModel = new AViewModel();
aViewModel.A = db.As.Find(id);
if (aViewModel.Receipt == null)
{
return HttpNotFound();
}
aViewModel.b = db.Bs.Where(x => x.aID == id.Value).ToList();
aViewModel.Vendor = db.Cs.Where(x => x.cID == aViewModel.a.cID).FirstOrDefault();
return View(aViewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(AViewModel aViewModel)
{
if (!canUserAccessA(aViewModel.a.aID) || aViewModel.a.UserID != WebSecurity.GetUserId(User.Identity.Name))
return new HttpStatusCodeResult(HttpStatusCode.Forbidden);
if (ModelState.IsValid)
{
db.Entry(aViewModel.a).State = EntityState.Modified; //THIS IS WHERE THE ERROR IS BEING THROWN
db.SaveChanges();
return RedirectToAction("Index");
}
return View(aViewModel);
}
如上行所示
db.Entry(aViewModel.a).State = EntityState.Modified;
抛出异常:
附加类型为'A‘的实体失败,因为同一类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为一些实体是新的,并且还没有接收到数据库生成的键值。在这种情况下,使用“Add”方法或“Added”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。
有没有人看到我的代码中有什么错误,或者知道在什么情况下会在编辑模型的过程中抛出这样的错误?
发布于 2014-04-23 02:58:31
问题解决了!
Attach
方法可能会对某些人有所帮助,但在这种情况下不会有帮助,因为在Edit GET控制器函数中加载文档时,已经在跟踪文档了。Attach将抛出完全相同的错误。
我在这里遇到的问题是由函数canUserAccessA()
引起的,它在更新对象a的状态之前加载A实体,这搞砸了被跟踪的实体,它将对象的状态更改为Detached
。
解决方案是修改canUserAccessA()
,这样我加载的对象就不会被跟踪。查询上下文时需要调用函数AsNoTracking()
。
// User -> Receipt validation
private bool canUserAccessA(int aID)
{
int userID = WebSecurity.GetUserId(User.Identity.Name);
int aFound = db.Model.AsNoTracking().Where(x => x.aID == aID && x.UserID==userID).Count();
return (aFound > 0); //if aFound > 0, then return true, else return false.
}
由于某种原因,我不能在AsNoTracking()
中使用.Find(aID)
,但这并不重要,因为我可以通过更改查询来实现同样的效果。
希望这篇文章能帮助任何有类似问题的人!
发布于 2015-08-02 19:20:34
有趣的是:
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);
或者,如果您仍然不是通用的:
_dbContext.Set<UserEntity>().AddOrUpdate(entityToBeUpdatedWithId);
似乎顺利地解决了我的问题。
发布于 2014-04-22 03:04:35
您尝试修改的实体似乎未被正确跟踪,因此未被识别为已编辑,而是添加了。
尝试执行以下操作,而不是直接设置状态:
//db.Entry(aViewModel.a).State = EntityState.Modified;
db.As.Attach(aViewModel.a);
db.SaveChanges();
另外,我想警告您,您的代码包含潜在的安全漏洞。如果您在视图模型中直接使用entity,则可能会有人通过在提交的表单中添加正确命名的字段来修改entity的内容。例如,如果用户添加了名为"A.FirstName“的输入框,并且实体包含该字段,则该值将被绑定到viewmodel并保存到数据库中,即使在应用程序的正常操作中不允许用户更改该值。
更新:
为了克服前面提到的安全漏洞,您不应该将域模型暴露为视图模型,而应使用单独的视图模型。然后,您的操作将收到视图模型,您可以使用AutoMapper等映射工具将其映射回域模型。这将保护您的安全,防止用户修改敏感数据。
下面是扩展的解释:
https://stackoverflow.com/questions/23201907
复制相似问题