首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ASP.NET MVC -附加'MODELNAME‘类型的实体失败,因为同一类型的另一个实体已具有相同的主键值

ASP.NET MVC -附加'MODELNAME‘类型的实体失败,因为同一类型的另一个实体已具有相同的主键值
EN

Stack Overflow用户
提问于 2014-04-22 00:58:46
回答 21查看 174.2K关注 0票数 137

简而言之,异常是在POSTing包装器模型期间抛出的,并将一个条目的状态更改为“已修改”。在更改状态之前,状态被设置为“Detached”,但调用Attach()确实会抛出相同的错误。我使用的是EF6。

请在下面找到我的代码(模型名称已更改,以便于阅读)

模型

代码语言:javascript
复制
// Wrapper classes
        public class AViewModel
        {
            public A a { get; set; }
            public List<B> b { get; set; }
            public C c { get; set; }
        }   

控制器

代码语言:javascript
复制
        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);
        }

如上行所示

代码语言:javascript
复制
db.Entry(aViewModel.a).State = EntityState.Modified;

抛出异常:

附加类型为'A‘的实体失败,因为同一类型的另一个实体已具有相同的主键值。如果图形中的任何实体具有冲突的键值,则在使用“Attach”方法或将实体的状态设置为“未更改”或“已修改”时,可能会发生这种情况。这可能是因为一些实体是新的,并且还没有接收到数据库生成的键值。在这种情况下,使用“Add”方法或“Added”实体状态来跟踪图形,然后根据需要将非新实体的状态设置为“未更改”或“已修改”。

有没有人看到我的代码中有什么错误,或者知道在什么情况下会在编辑模型的过程中抛出这样的错误?

EN

回答 21

Stack Overflow用户

回答已采纳

发布于 2014-04-23 02:58:31

问题解决了!

Attach方法可能会对某些人有所帮助,但在这种情况下不会有帮助,因为在Edit GET控制器函数中加载文档时,已经在跟踪文档了。Attach将抛出完全相同的错误。

我在这里遇到的问题是由函数canUserAccessA()引起的,它在更新对象a的状态之前加载A实体,这搞砸了被跟踪的实体,它将对象的状态更改为Detached

解决方案是修改canUserAccessA(),这样我加载的对象就不会被跟踪。查询上下文时需要调用函数AsNoTracking()

代码语言:javascript
复制
// 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),但这并不重要,因为我可以通过更改查询来实现同样的效果。

希望这篇文章能帮助任何有类似问题的人!

票数 166
EN

Stack Overflow用户

发布于 2015-08-02 19:20:34

有趣的是:

代码语言:javascript
复制
_dbContext.Set<T>().AddOrUpdate(entityToBeUpdatedWithId);

或者,如果您仍然不是通用的:

代码语言:javascript
复制
_dbContext.Set<UserEntity>().AddOrUpdate(entityToBeUpdatedWithId);

似乎顺利地解决了我的问题。

票数 116
EN

Stack Overflow用户

发布于 2014-04-22 03:04:35

您尝试修改的实体似乎未被正确跟踪,因此未被识别为已编辑,而是添加了。

尝试执行以下操作,而不是直接设置状态:

代码语言:javascript
复制
//db.Entry(aViewModel.a).State = EntityState.Modified;
db.As.Attach(aViewModel.a); 
db.SaveChanges();

另外,我想警告您,您的代码包含潜在的安全漏洞。如果您在视图模型中直接使用entity,则可能会有人通过在提交的表单中添加正确命名的字段来修改entity的内容。例如,如果用户添加了名为"A.FirstName“的输入框,并且实体包含该字段,则该值将被绑定到viewmodel并保存到数据库中,即使在应用程序的正常操作中不允许用户更改该值。

更新:

为了克服前面提到的安全漏洞,您不应该将域模型暴露为视图模型,而应使用单独的视图模型。然后,您的操作将收到视图模型,您可以使用AutoMapper等映射工具将其映射回域模型。这将保护您的安全,防止用户修改敏感数据。

下面是扩展的解释:

http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/

票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23201907

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档