简而言之,在发布包装器模型并将一个条目的状态更改为“已修改”期间引发异常。更改状态之前,状态设置为“已分离”,但调用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”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新的,尚未收到数据库生成的键值。在这种情况下,请使用“Add”方法或“Added”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“Unchanged”或“Modified”。
有没有人在我的代码中看到任何错误,或者了解在什么情况下它会在编辑模型时抛出这样的错误?
您试图修改的实体似乎未被正确跟踪,因此不会被识别为已编辑,而是已添加。
不要直接设置状态,请尝试执行以下操作:
//db.Entry(aViewModel.a).State = EntityState.Modified;
db.As.Attach(aViewModel.a);
db.SaveChanges();
此外,我想警告您,您的代码包含潜在的安全漏洞。如果您直接在视图模型中使用实体,那么您可能会有人通过在提交的表单中添加正确命名的字段来修改实体的内容。例如,如果用户添加了名称为“A. FirstName”的输入框,并且实体包含此类字段,那么该值将绑定到视图模型并保存到数据库,即使用户在应用程序的正常操作中不允许更改该值。
更新:
为了克服前面提到的安全漏洞,永远不要将域模型公开为viewmodel,而应使用单独的viewmodel。然后,您的操作将收到viewmodel,您可以使用一些映射工具(如AutoMapper)将其映射回域模型。这样可以防止用户修改敏感数据。
以下是扩展说明:
http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/
有趣的是:
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);
或者,如果您仍然不是通用的:
_dbContext.Set<UserEntity>().AddOrUpdate(entityToBeUpdatedWithId);
似乎顺利解决了我的问题。
问题解决!
Attach
方法可能会帮助某人,但在这种情况下没有帮助,因为在Edit GET控制器函数中加载文档时已经在跟踪文档。附加会抛出完全相同的错误。
我在这里遇到的问题是由函数canUserAccessA()引起的,该函数在更新对象A的状态之前加载了A实体。这将破坏跟踪的实体,并将对象的状态更改为分离状态。
解决方案是修改canUserAccessA()
,这样我正在加载的对象就不会被跟踪。查询上下文时应调用函数AsNoTrack()
。
// 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()查找(aID)
,但这并不重要,因为我可以通过更改查询来实现同样的效果。
希望这将帮助任何人有类似的问题!
让我快速描述一下我的问题。 我有5个客户的5个数据库,每个数据库都有一个名为SubnetSettings的表。 我已经创建了一个下拉列表来选择一个客户,并将显示属于所选客户的SubnetSSet表,并允许我创建、编辑和删除。 我可以毫无问题地创建、删除,但当我想编辑数据时,它会带来错误: /运输管理系统应用程序中的服务器错误。 附加“CFS”类型的实体。领域实体。SubnetSettings“失败
错误消息:附加类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。 问题:我如何以类似于下面代码中AttachActivity方法所示的方式附加实体? 我必须假设上面错误消息的“另一个实体”部分指的是内存中存在但超出范围的对象(?)。我之所以注意到这一点,是因为我尝试附加的实体类型的DBSet的本地属性返回零。 我有理由相信实体不存在于上下文中,因为我遍历代码并在创建上下文时观察上下文。实
错误消息: 正在附加“FaridCRMData”类型的实体。模型。“Customer”失败,因为相同类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的 我的代码: FactorService. BaseService.cs:
我的WebApi应用程序有一个小问题。当我将新实体添加到我的数据库时,会出现这个问题。 实体: 要在其中向数据库中添加新赌注实体的代码: 在此位置,将我的实体添加到DB中(DB:我的SQL) 调用save方法时,代码会引发以下异常: 附加“WebCar. Domain. Entities. Lot”类型的实体失败,因为另一个相同类型的实体已经具有相同的主键值。如果图形中的任何实体具有冲突的键值,则
如何修复错误... 更新记录显示此错误时: 显示错误:{“附加“DomainClass.WorkshopReport”类型的实体失败,因为相同类型的另一个实体已具有相同的主键值。如果图中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体状态设置为“Unchanged”或“Modified”时可能会发生这种情况。这可能是因为某些实体是新实体,尚未收到生成的数据库关键值。在这种情况下,请使
错误: 其他信息:附加“实体”类型的实体失败。客户”,因为另一个相同类型的实体已经具有相同的主键值。如果图中的任何实体具有冲突的键值,则使用“附加”方法或将实体的状态设置为“未更改”或“修改”时可能会发生这种情况。这可能是因为某些实体是新的并且没有收到数据库生成的键值。在这种情况下,使用“添加”方法或“添加”实体来跟踪图,然后根据需要将非新实体的状态设置为“未更改”或“修改”。 我的代码: 我是这