如何设置多对多关系的引用集合?

时间:2014-04-12 13:15:32

标签: c# asp.net-mvc entity-framework asp.net-mvc-5 entity-framework-6

我必须拥有多对多关系的实体。

public class M1 { M1Id int; ICollection<M2> M2s { get; set; } }
public class M2 { M2Id int; ICollection<M1> M1s { get; set; } }
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
    public DbSet<M1> M1s{ get; set; }
    public DbSet<M2> M2s{ get; set; }
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<M1>().HasMany<M2>(e => e.M2s).WithMany(c => c.M1s)
            .Map(c =>
            {
                c.MapLeftKey("M1"); 
                c.MapRightKey("M2Id"); 
                c.ToTable("M1AndM2");
            });

我需要在post方法中从VM创建M1

var M1 = new M1
{
    Id = M1Vm.M1Id,
    // ....
    M2s = db.M1.FirstOrDefault(x => x.M1Id == M1Vm.M1Id).M2s
    if (ModelState.IsValid)
    {
        db.Entry(M1).State = EntityState.Modified; // Error
        UpdateM2s(M1, M1Vm.NewM2s); // Update table M1AndM2

设置State的{​​{1}}时会出现以下错误。

  

ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法使用相同的键跟踪多个对象。

比较后,M1将运行UpdateM2sM1.M2s.Add(m2)。以下是代码。

M1.M2s.Remove(m2)

1 个答案:

答案 0 :(得分:2)

线......

M2s = db.M1.FirstOrDefault(x => x.M1Id == M1Vm.M1Id).M2s

...从数据库加载M1实体和密钥M1Vm.M1Id,并将其附加到上下文。然后它通过延迟加载加载其M2s集合。与此同时,您正在创建一个具有相同键M1的新实体Id = M1Vm.M1Id,并通过将其状态设置为db.Entry(M1).State = EntityState.Modified将其附加到上下文中。因此,您有两个具有相同键的对象附加到上下文,这是异常所抱怨的。

您可以尝试通过仅加载M2s来解决问题,而不使用父级:

M2s = db.M1.Select(m1 => m1.M2s).FirstOrDefault(x => x.M1Id == M1Vm.M1Id)

但是,我认为更好的方法是根本不创建新对象,而是从包含M1集合的数据库中加载原始M2s,然后使用视图更新对象图模型:

if (ModelState.IsValid)
{
    var m1 = db.M1.Include(m => m.M2s).FirstOrDefault(x => x.M1Id == M1Vm.M1Id);
    db.Entry(m1).CurrentValues.SetValues(M1Vm);
    UpdateM2(m1, M1Vm.NewM2s);
    db.SaveChanges();
}

修改

现在我看到了UpdateM2方法,我建议您也改变它。在else部分中,您要创建一个新的M2,其密钥与另一个已包含并附加在m1查询中的m2相同。因此,您再次使用相同的密钥获得有关两个附加对象的相同异常,这次只是引用M2而不是M1。您可以尝试更改else块,如下所示:

else if (!newM2.Assigned)
{
    var m2 = m1.M2s.SingleOrDefault(c => c.Id == newM2.Id);
    if (m2 != null)
    {
        m1.M2s.Remove(m2);
    }
}