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.