entity framework,更新相关对象的问题
我目前正在使用entity framework的最新版本的项目,我遇到了一个我似乎无法解决的问题。
当涉及到更新现有对象时,我可以很容易地更新对象属性,直到涉及到另一个类的引用。
在下面的例子中,我有一个名为Foo的类,它存储各种属性,其中2个是其他类的实例
public class Foo { public int Id {get; set;} public string Name {get; set;} public SubFoo SubFoo {get; set} public AnotherSubFoo AnotherSubFoo {get; set} }
当我使用下面的Edit方法时,我传递了我希望更新的对象,并且我可以设法让Name正确更新,但是我没有设法find一种方法来获取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; }
任何帮助或指针将不胜感激。
更新:根据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; }
现在运行这个,我得到错误
实体typesCollection`1不是当前上下文模型的一部分。
为了尝试解决这个问题,我添加了一些代码来尝试将newFoo子类附加到上下文中,但是通过一个错误说明ObjectManager已经拥有了一个相同的实体
ObjectStateManager中已经存在具有相同键的对象。 ObjectStateManager不能使用同一个键跟踪多个对象
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; }
如果关系可能已经完全被删除或者已经被创build,那么你也需要明确地处理这些情况:
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; }
只是以为我会张贴下面的链接,因为它确实帮助我了解如何更新相关的实体。
用aspnetworkingmvc应用程序中的entity framework更新相关数据
注:我确实改变了UpdateInstructorCourses函数中显示的逻辑,以适应我的需要。
您也可以独立调用表格
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();