我目前正在使用最新版本的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
正确更新,但是我还没有设法找到方法哪个可以改变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;
}
现在运行时,我收到错误:
实体类型Collection`1不是当前模型的一部分 上下文。
为了尝试解决这个问题,我添加了代码以尝试将newFoo
子类附加到上下文中,但这通过错误说明ObjectManager
已经有一个实体相同:
ObjectStateManager中已存在具有相同键的对象。 ObjectStateManager无法跟踪具有相同对象的多个对象 键
答案 0 :(得分:92)
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;
}
答案 1 :(得分:3)
我想我会发布下面的链接,因为它确实帮助我了解如何更新相关实体。
Updating related data with the entity framework in an asp net mvc application
注意:我确实稍微更改了UpdateInstructorCourses函数中显示的逻辑以满足我的需要。
答案 2 :(得分: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();