Entity Framework Update Entity вместе с дочерними объектами (добавьте/обновите по мере необходимости)

У меня есть отношение "много-ко-многим" между Issues и Scopes в моем EF-контексте. В 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.

Что я делаю неправильно?

Ответы

Ответ 1

Заготовки обычно эффективны только для отношений 1-*. *-* отношения вводят другой набор проблем.

А именно, когда вы присоединяете оба конца - в отличие от 1-* - вы все еще не знаете, существует ли связь или нет.

Значит, этот код:

if (issue.Scopes.Contains(thisScope))

Вероятно, каждый раз возвращается false.

Что бы я сделал, так это:

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.

то есть. если у вас есть набор идентификаторов области действия, которые вы хотите связать с проблемой (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

Microsoft

Ответ 2

Предупреждение, это только с головы, я не пробовал.

Я не думаю, что вы можете установить внешний ключ так же, как и с ProjectID.

вам нужно получить проект и добавить его в область действия scope.Project. EF будет следить за отношениями при вставке.

Теперь снова я не пытался и мог ошибаться, но стоит попробовать. Может быть, это поможет вам в пути.

Ответ 3

** Отказ от ответственности: я относительно новичок в EF, беру свой ответ с ведром соли.

Если это форма редактирования, я думаю, что ваш объект проблемы не должен быть "новой" проблемой, он должен вытащить проблему из хранилища данных. Насколько я понимаю,

Issue issue = new Issue { IssueID = id, TSColumn = formIssue.TSColumn }; edmx.Issues.Attach(issue); вы эффективно создаете новый номер с идентификатором того, который вы пытаетесь отредактировать.

Опять же, я здесь ищу способы понять EF сам. Иногда я пропускаю свои инструкции SQL.