更新具有多对多关系的断开连接的实体



假设我在实体框架代码优先设置中有以下模型类:

public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Team> Teams { get; set; }
}
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Person> People { get; set; }
}

从此代码创建的数据库包括一个 TeamPeople 表,该表表示人员和团队之间的多对多关系。

现在假设我有一个断开连接的 Person 对象(不是代理,尚未附加到上下文),其 Teams 集合包含一个或多个断开连接的团队对象,所有这些对象都表示数据库中已有的 Teams。例如,如果数据库中已存在 ID 为 1 的人员以及 ID 为 3 的团队,则由以下对象创建:

var person = new Person
{
Id = 1,
Name = "Bob",
Teams = new HashSet<Team>
{
new Team { Id = 3, Name = "C Team"}
}
};

更新此对象的最佳方法是什么,以便在更新后 TeamPersons 表包含 Bob 的单行,将他链接到 C 团队?我已经尝试了显而易见的方法:

using (var context = new TestContext())
{
context.Entry(person).State = EntityState.Modified;
context.SaveChanges();
}

但是团队集合只是被忽略了。我也尝试了其他各种事情,但似乎没有什么能完全符合我在这里所追求的。感谢您的任何帮助。

编辑:

所以我得到我可以从数据库中获取人员和团队,更新它们,然后提交更改:

using (var context = new TestContext())
{
var dbPerson = context.People.Find(person.Id);
dbPerson.Name = person.Name;
dbPerson.Teams.Clear();
foreach (var id in person.Teams.Select(x => x.Id))
{
var team = context.Teams.Find(id);
dbPerson.Teams.Add(team);
}
context.SaveChanges();
}

但是,如果人是一个复杂的实体,这是一种痛苦。我知道我可以使用 Automapper 或其他东西来让事情变得更容易一些,但如果无法保存原始 person 对象,而不必获取一个新的对象并复制所有属性,这似乎仍然是一种耻辱......

一般的方法是从数据库中获取Team并将其AddPersonTeams集合中。设置EntityState.Modified仅影响标量属性,而不会影响导航属性。

首先尝试选择现有实体,然后将团队附加到 Person 对象的团队集合。

像这样的东西:(语法可能不完全正确)

using (var context = new TestContext())
{
var person = context.Persons.Where(f => f.Id == 1).FirstOrDefault();
var team = context.Teams.Where(f => f.Id == 3).FirstOrDefault();
person.Teams.Add(team);
context.Entry(person).State = EntityState.Modified;
context.SaveChanges();
}

这就是 EF s**ks 的地方。 对于断开连接的场景,效率非常低。 加载更新/删除的数据以及每个用于重新附加更新的数据,不能只是将更新的实体附加到上下文中,因为具有相同键的实体可能已经存在于上下文中,在这种情况下,EF 只会抛出。 需要做的是检查具有相同键的实体是否已在上下文中并附加或相应更新。更新具有多对多关系子项的实体更糟糕。删除已删除的子项是从子项的实体集中删除的,但不是引用属性,这非常混乱。

可以使用 Attach 方法。试试这个:

using (var context = new TestContext())
{
context.People.Attach(person);
//i'm not sure if this foreach is necessary, you can try without it to see if it works
foreach (var team in person.Teams)
{
context.Teams.Attach(team);
}
context.Entry(person).State = EntityState.Modified;
context.SaveChanges();
}

我没有测试此代码,如果您有任何问题,请告诉我

最新更新