2012-11-05 67 views
45

我目前正在使用实体框架的最新版本的项目,我遇到了一个问题,我似乎无法解决。实体框架,更新相关对象的问题

当涉及到更新现有对象时,我可以很容易地更新对象属性,直到涉及到对另一个类的引用的属性。

在下面的例子中,我有一个叫做Foo类,其存储各种性质,与其它类

public class Foo 
{ 
    public int Id {get; set;} 
    public string Name {get; set;} 
    public SubFoo SubFoo {get; set} 
    public AnotherSubFoo AnotherSubFoo {get; set} 
} 

的这些是实例2当我使用下面编辑方法,我通过在对象我希望更新,我可以设法让名称正确更新,但是我还没有设法找到一种方法来获得SubFoo的属性改变。例如,如果SubFoo类具有Name属性,并且已更改,并且在我的数据库和newFoo之间不同,它不会更新。

public Foo Edit(Foo newFoo) 
{ 

     var dbFoo = context.Foo 
          .Include(x => x.SubFoo) 
          .Include(x => x.AnotherSubFoo) 
          .Single(c => c.Id == newFoo.Id); 



     var entry = context.Entry<Foo>(dbFoo); 
     entry.OriginalValues.SetValues(dbFoo); 
     entry.CurrentValues.SetValues(newFoo); 

     context.SaveChanges(); 

     return newFoo; 
} 

任何帮助或指针将不胜感激。

UPDATE: 基于由Slauma评论我已经修改了我的方法

public Foo Edit(Foo newFoo) 
{ 

     var dbFoo = context.Foo 
          .Include(x => x.SubFoo) 
          .Include(x => x.AnotherSubFoo) 
          .Single(c => c.Id == newFoo.Id); 



     context.Entry(dbFoo).CurrentValues.SetValues(newFoo); 
     context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); 

     context.SaveChanges(); 

     return newFoo; 
} 

现在,当运行这个,我得到的错误

的实体类型Collection`1不是一部分的模型为当前的 上下文。

为了避免这个搞定了,我添加了代码以尝试将newFoo子类附加到上下文,但通过一个错误,指出该的ObjectManager已经有了一个实体相同

对象在ObjectStateManager中已经存在相同的密钥。 的ObjectStateManager不能用相同的 关键

回答

77

CurrentValues.SetValues只更新标量的属性,但没有相关的实体跟踪多个目标,所以你必须为每个相关实体做相同的:

public Foo Edit(Foo newFoo) 
{ 
    var dbFoo = context.Foo 
         .Include(x => x.SubFoo) 
         .Include(x => x.AnotherSubFoo) 
         .Single(c => c.Id == newFoo.Id); 

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo); 
    context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); 
    context.Entry(dbFoo.AnotherSubFoo).CurrentValues.SetValues(newFoo.AnotherSubFoo); 

    context.SaveChanges(); 

    return newFoo; 
} 

如果关系可能已被完全删除或已创建您还需要明确处理这些情况:

public Foo Edit(Foo newFoo) 
{ 
    var dbFoo = context.Foo 
         .Include(x => x.SubFoo) 
         .Include(x => x.AnotherSubFoo) 
         .Single(c => c.Id == newFoo.Id); 

    context.Entry(dbFoo).CurrentValues.SetValues(newFoo); 
    if (dbFoo.SubFoo != null) 
    { 
     if (newFoo.SubFoo != null) 
     { 
      if (dbFoo.SubFoo.Id == newFoo.SubFoo.Id) 
       // no relationship change, only scalar prop. 
       context.Entry(dbFoo.SubFoo).CurrentValues.SetValues(newFoo.SubFoo); 
      else 
      { 
       // Relationship change 
       // Attach assumes that newFoo.SubFoo is an existing entity 
       context.SubFoos.Attach(newFoo.SubFoo); 
       dbFoo.SubFoo = newFoo.SubFoo; 
      } 
     } 
     else // relationship has been removed 
      dbFoo.SubFoo = null; 
    } 
    else 
    { 
     if (newFoo.SubFoo != null) // relationship has been added 
     { 
      // Attach assumes that newFoo.SubFoo is an existing entity 
      context.SubFoos.Attach(newFoo.SubFoo); 
      dbFoo.SubFoo = newFoo.SubFoo; 
     } 
     // else -> old and new SubFoo is null -> nothing to do 
    } 

    // the same logic for AnotherSubFoo ... 

    context.SaveChanges(); 

    return newFoo; 
} 

如果关系已经改变了标量属性,您最终还需要将附加实体的状态设置为Modified

编辑

如果 - 根据您的评论 - Foo.SubFoo实际上是一个集合而不是只是一个参考,你需要这样的更新相关实体:

public Foo Edit(Foo newFoo) 
{ 
    var dbFoo = context.Foo 
         .Include(x => x.SubFoo) 
         .Include(x => x.AnotherSubFoo) 
         .Single(c => c.Id == newFoo.Id); 

    // Update foo (works only for scalar properties) 
    context.Entry(dbFoo).CurrentValues.SetValues(newFoo); 

    // Delete subFoos from database that are not in the newFoo.SubFoo collection 
    foreach (var dbSubFoo in dbFoo.SubFoo.ToList()) 
     if (!newFoo.SubFoo.Any(s => s.Id == dbSubFoo.Id)) 
      context.SubFoos.Remove(dbSubFoo); 

    foreach (var newSubFoo in newFoo.SubFoo) 
    { 
     var dbSubFoo = dbFoo.SubFoo.SingleOrDefault(s => s.Id == newSubFoo.Id); 
     if (dbSubFoo != null) 
      // Update subFoos that are in the newFoo.SubFoo collection 
      context.Entry(dbSubFoo).CurrentValues.SetValues(newSubFoo); 
     else 
      // Insert subFoos into the database that are not 
      // in the dbFoo.subFoo collection 
      dbFoo.SubFoo.Add(newSubFoo); 
    } 

    // and the same for AnotherSubFoo... 

    db.SaveChanges(); 

    return newFoo; 
} 
+0

感谢您的评论,我已经发布了一个更新我的问题 – Thewads

+0

@Thewads:我只能想象,你得到这个“*集合......是不是模型的一部分... *“错误,当'Foo.SubFoo'是一个*集合*类型。但它只是一个引用'公众SubFoo SubFoo {get;在示例模型中设置},而不是集合。它真的没有收藏?我的回答是基于你当前的模型,其中'SubFoo'不是一个集合。 – Slauma

+0

对不起,我没有意识到有收藏的模型会有所作为......我的错误。在我处理的模型中:public ICollection SubFoo {get;设置;} – Thewads

0

您也可以致电表独立

MyContext db = new MyContext 
// I like using asynchronous calls in my API methods 
var OldFoo = await db.Foo.FindAsync(id); 
var OldAssociateFoo = db.AssociatedFoo; 
var NewFoo = OldFoo; 
var NewAssociatedFoo = OldAssociatedFoo; 

NewFoo.SomeValue = "The Value"; 
NewAssociatedFoo.OtherValue = 20; 

db.Entry(OldFoo).CurrentValues.SetValues(NewFoo); 
db.Entry(OldAssociatedFoo).CurrentValues.SetValues(NewAssociatedFoo); 

await db.SaveChangesAsync();