我必须拥有多对多关系的实体。
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
将运行UpdateM2s
或M1.M2s.Add(m2)
。以下是代码。
M1.M2s.Remove(m2)
答案 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);
}
}