使用多对多关系更新断开连接的实体

时间:2012-11-15 20:36:50

标签: c# entity-framework ef-code-first many-to-many dbcontext

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

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; }
}

根据此代码创建的数据库包含一个TeamPersons表,表示人员和团队之间的多对多关系。

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

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

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

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

但是Teams系列被忽略了。我也尝试了其他各种各样的东西,但似乎没有什么能像我在这里完成的那样。谢谢你的帮助。

修改

所以我知道我可以从数据库中获取Person和Team [s],更新它们然后提交更改:

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或其他东西使事情变得更容易,但如果没有办法保存原始人物对象,而不是必须得到一个新的并且复制所有属性,那么看起来仍然是一种耻辱...... / p>

4 个答案:

答案 0 :(得分:1)

一般方法是从数据库中获取Team,并将Add提取到Person的{​​{1}}集合。设置Teams仅影响标量属性,而不影响导航属性。

答案 1 :(得分:0)

首先尝试选择现有实体,然后将团队附加到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();
}

答案 2 :(得分:0)

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

答案 3 :(得分:0)

您可以使用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();
}

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