【问题标题】:New subentity will not save when parent entity is saved保存父实体时不会保存新子实体
【发布时间】:2014-08-03 21:53:32
【问题描述】:

我有一个父实体

foo 存在于数据库中,我在此实体上有一个属性 bar(一对多关系)。

Foo 是分离的,因为它使用 WebApi 反序列化,所以我这样做是为了 foo

context.Foos.AddOrUpdate(foo);

即使附加了一个新的bar 引用,它也不会被保存,但是对于我们拥有的另一个关系,它确实是这样工作的,它是一个多对多关系。如果我向该集合添加一个新实体,它将被保存到其表中,并且还会向关系表中添加一行。

如果我在调用context.Foos.AddOrUpdate(foo); 之前执行context.Bars.AddOrUpdate(foo.Bar);,它将正确地将新bar 保存到bar 表中,但不会将正确的barId 添加到foo 表中

@Yuliam Chandra

如果我正确理解了您的答案(我认为您在答案中混淆了 bar 和 foos),这应该可以吗?

var foo = new Foo();
foo.FooId = 524 //Existing foo;

foo.Bar = new Bar(); //Completely new bar    
db.Foos.AddOrUpdate(foo);
db.SaveChanges();

但事实并非如此

【问题讨论】:

  • 新栏是如何添加到 foo 中的?调用 context.Foos.AddOrUpdate(foo) 时,bar 上是否设置了外键?
  • Bar 没有外键,Foo 有。 Foo.BarId
  • 那么 - 在你的“一对多”中,你有一个酒吧对多个 Foos?
  • 是的,没错,(多对一)

标签: c# entity-framework entity-framework-6.1


【解决方案1】:

处理断开的对象需要额外的代码。

如果您正在处理断开连接的对象,则必须手动管理 同步。

Source

如果Bar 是现有实体,您需要先附加它,所以Foo 将被添加为Bar's 子实体。

if (foo.Bar.Id != 0)
{
    context.Bars.Attach(foo.Bar);
    context.Foos.AddOrUpdate(foo);
}

上述代码示例与this article中的Course (Foo) 和Department (Bar) 示例类似。

但如果Bar是一个新实体,你只需添加Foo,那么Bar也会被添加。

else
{
    context.Foos.Add(foo);
}

其他品种可以在my answer上查看。

更新

在进一步解释之前,我想显示相同的代码。

  • db.Set<T>().Add(instance) 等于 db.Entry(instance).State = EntityState.Added;
  • db.Set<T>().Attach(instance) 等于 db.Entry(instance).State = EntityState.Unchanged;

上一个答案解释了两个条件。

  • 新的 Foo 和现有的 Bar

    context.Bars.Attach(foo.Bar);

    context.Foos.AddOrUpdate(foo);

  • 新的 Foo 和新的 Bar

    context.Foos.Add(foo);

特别添加

Added 具有特殊条件,一旦实体被标记为Added。图中的所有实体也将被标记为Added,即使任何引用实体是数据库中的现有对象。如果我们有这个代码,就会添加 Foo 和 Bar。

var foo = new Foo (); // new foo
foo.Bar = new Bar { BarId = 123 }; // existing bar
db.Set<Foo>().Add(foo);
db.SaveChanges();

为了防止这种情况发生,需要先附加 Bar。

var foo = new Foo (); // new foo
foo.Bar = new Bar { BarId = 123 }; // existing bar
db.Set<Bar>().Attach(bar);
db.Set<Foo>().Add(foo);
db.SaveChanges();

查看Julie Lerman article 以获得更完整的说明。

发生这种情况的原因是当您使用 DbSet.Add 方法时(即 即,Screencasts.Add),不仅标记了根实体的状态 “已添加”,但图中没有上下文的所有内容 之前知道的也标记为已添加。尽管开发商 可能知道该主题有一个现有的 Id 值,实体框架 尊重其 EntityState(已添加)并创建插入数据库命令 对于主题,不管现有的 Id。

根据您的更新调整说明

您更新的问题是关于现有的 Foo 和新的 Bar。使用您的代码,结果不会添加新的 Bar 并且现有的 Foo 不会与新的 Bar 建立关系。

var foo = new Foo();
foo.FooId = 524 //Existing foo;

foo.Bar = new Bar(); //Completely new bar    
db.Foos.AddOrUpdate(foo);
db.SaveChanges();

如果我们手动将Bar与AddOrUpdate(foo)一起添加,结果仍然不如预期。新的 Bar 会被添加,但 Foo 不会与新的 Bar 有关系。

db.Bars.Add(foo.Bar);
db.Foos.AddOrUpdate(foo);

AddOrUpdate 的行为不一致。

解决方案

如果您正在处理断开连接的对象,则必须手动管理 同步。

此代码应适用于所有条件。

  • 新 Foo 和新酒吧
  • 新的 Foo 和现有的酒吧
  • 现有 Foo 和新酒吧
  • 现有的 Foo 和现有的 Bar

这段代码取决于id(id = 0 是一个新实体)。

db.Entry(foo).State = 
       foo.FooId == 0 ? EntityState.Added : EntityState.Modified;
if (foo.Bar != null)
{
    db.Entry(foo.Bar).State = 
       foo.Bar.BarId == 0 ? EntityState.Added : EntityState.Modified;
                                                             ^^^
    // If you don't want to change the Bar while creating a relationship between
    // Foo and with existing Bar, you can change 
    // `EntityState.Modified` with `EntityState.Unchanged`
}
db.SaveChanges();

与AddOrUpdate相关,我认为有一个未解决的问题可以在here找到。

【讨论】:

  • 嗯,我想我试过了。将在周末尝试更多并让您知道
  • @Anders,我更新了答案以进一步解释您的更新
  • 听起来 nHibernate 仍然领先于 EF :D 会尝试这个并回复您,感谢您的所有帮助!
  • 它有效,谢谢!感觉与分离实体一起工作不需要这么复杂。必须是 CRUD 系统中非常常见的场景,您从客户端对实体进行序列化/反序列化
  • @Anders,是的,遗憾的是目前 EF 没有处理断开连接的对象的扩展方法
猜你喜欢
  • 2017-03-15
  • 2017-08-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多