实体框架更新实体以及子实体(根据需要添加/更新)

时间:2010-05-07 14:00:48

标签: asp.net-mvc entity-framework

我的EF上下文中IssuesScopes之间存在多对多关系。在ASP.NET MVC中,我打开了一个允许用户编辑特定问题的编辑表单。在表单的底部,是一个复选框列表,允许他们选择适用于此问题的范围。在编辑问题时,它可能总是会有一些与之关联的范围 - 这些框将被检查。但是,用户有机会检查更多范围或删除一些当前检查的范围。我的代码看起来像这样保存只是问题

            using (var edmx = new MayflyEntities())
            {
                Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn };
                edmx.Issues.Attach(issue);

                UpdateModel(issue);

                if (ModelState.IsValid)
                {
                    //if (edmx.SaveChanges() != 1) throw new Exception("Unknown error. Please try again.");
                    edmx.SaveChanges();
                    TempData["message"] = string.Format("Issue #{0} successfully modified.", id);
                }
            }

所以,当我尝试添加逻辑来保存相关的范围时,我尝试了几件事,但最终,这对我来说是最有意义的:

            using (var edmx = new MayflyEntities())
            {
                Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn };
                edmx.Issues.Attach(issue);

                UpdateModel(issue);

                foreach (int scopeID in formIssue.ScopeIDs)
                {
                    var thisScope = new Scope { ID = scopeID };
                    edmx.Scopes.Attach(thisScope);
                    thisScope.ProjectID = formIssue.ProjectID;
                    if (issue.Scopes.Contains(thisScope))
                    {
                        issue.Scopes.Attach(thisScope); //the scope already exists
                    }
                    else
                    {
                        issue.Scopes.Add(thisScope); // the scope needs to be added
                    }
                }

                if (ModelState.IsValid)
                {
                    //if (edmx.SaveChanges() != 1) throw new Exception("Unknown error. Please try again.");
                    edmx.SaveChanges();
                    TempData["message"] = string.Format("Issue #{0} successfully modified.", id);
                }
            }

但不幸的是,这只会引发以下异常:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

我做错了什么?

3 个答案:

答案 0 :(得分:12)

存根通常仅对1-*关系有效。 *-*关系引入了一系列不同的挑战。

即当你附加两端时 - 与1-*不同 - 你仍然不知道这种关系是否已经存在。

这意味着这段代码:

if (issue.Scopes.Contains(thisScope))

每次都可能会返回假。

我要做的是:

edmx.Issues.Attach(issue);
UpdateModel(issue);
// or ctx.LoadProperty(issue, "Scopes") if it is a POCO class.
issue.Scopes.Load(); // hit the database to load the current state.

现在你需要找出你需要添加的东西&从issue.Scopes中删除。 您可以通过基于ID进行比较来完成此操作。

即。如果您有一组要与问题相关的范围ID(relatedScopes)

然后,此代码将确定要添加的内容和要删除的内容。

int[] toAdd = relatedScopes.Except(issue.Scopes.Select(s => s.ID)).ToArray();
int[] toRemove = issue.Scopes.Select(s => s.ID).Except(relatedScopes).ToArray();

现在为了添加你这样做:

foreach(int id in toAdd)
{
   var scope = new Scope{Id = id};
   edmx.Scopes.Attach(scope);
   issue.Scopes.Add(scope);
}

对于您需要删除的每个范围

foreach(int id in toRemove)
{
   issue.Scopes.Remove(issue.Scopes.Single(s => s.ID == id));
}

现在应该形成正确的关系。

希望这有帮助

Alex

微软

答案 1 :(得分:0)

警告,这只是我的头脑,我没有尝试过。

我认为您不能像使用ProjectID那样设置外键。

您需要检索项目并将其添加到scope.Project导航属性中。插入时EF会处理关系。

现在再次,我没有尝试也可能是错的,但值得一试。也许这有助于让你在途中......

答案 2 :(得分:0)

**免责声明:我对EF相对较新,用一桶盐来回答我的答案。

如果这是一个编辑表单,我认为您的问题对象不应该是一个“新”问题,它应该将问题从数据存储中拉出来。据我所知:

Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn }; edmx.Issues.Attach(issue); 您正在使用您尝试编辑的ID的ID来有效地创建新问题。

同样,我在这里寻找自己理解EF的方法。有时我想念我的SQL语句。