C# 实体框架,更新相关对象的问题
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13236116/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Entity framework, problems updating related objects
提问by Thewads
I am currently working on a project using the latest version of Entity Framework and I have come across an issue which I can not seem to solve.
我目前正在使用最新版本的实体框架开展一个项目,但遇到了一个我似乎无法解决的问题。
When it comes to updating existing objects, I can fairly easily update the object properties ok, until it comes to a property which is a reference to another class.
在更新现有对象时,我可以相当轻松地更新对象属性,直到涉及到另一个类的引用的属性。
In the below example I have a class called Foo, which stores various properties, with 2 of these being instances of other classes
在下面的示例中,我有一个名为 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}
}
When I use the below Edit()method, I pass in the object I wish to update and I can manage to get the Nameto properly update, however I have not managed to find a way in which to get the properties of the SubFoo to change. For example, if the SubFooclass has a property of Name, and this has been changed and is different between my DB and the newFoo, it does not get updated.
当我使用下面的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;
}
Any help or pointers would be greatly appreciated.
任何帮助或指示将不胜感激。
UPDATE: Based on the comment by Slauma I have modified my method to
更新:根据 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;
}
When running this now, I get the error:
现在运行它时,我收到错误:
The entity type Collection`1 is not part of the model for the current context.
实体类型 Collection`1 不是当前上下文模型的一部分。
To try and get around this, I added code to try to attach the newFoosubclasses to the context, but this through an error saying that the ObjectManageralready had an entity the same:
为了尝试解决这个问题,我添加了代码来尝试将newFoo子类附加到上下文,但这是通过一个错误说明ObjectManager已经有一个相同的实体:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
ObjectStateManager 中已存在具有相同键的对象。ObjectStateManager 无法跟踪具有相同键的多个对象
采纳答案by Slauma
CurrentValues.SetValuesonly updates scalar properties but no related entities, so you must do the same for each related entity:
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;
}
If the relationship could have been removed altogether or have been created you also need to handle those cases explicitly:
如果关系可以完全删除或已经创建,您还需要明确处理这些情况:
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;
}
You eventually also need to set the state of the attached entities to Modifiedif the relationship has been changed andthe scalar properties as well.
您最终还需要将附加实体的状态设置为Modified关系是否已更改以及标量属性是否已更改。
Edit
编辑
If - according to your comment - Foo.SubFoois actually a collection and not only a reference you will need something like this to update the related entities:
如果 - 根据您的评论 -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;
}
回答by craigvl
Just thought I would post the link below as it really helped me understand how to update related entities.
只是想我会发布下面的链接,因为它确实帮助我了解如何更新相关实体。
Updating related data with the entity framework in an asp net mvc application
NOTE: I did change the logic slightly that is shown in the UpdateInstructorCourses function to suit my needs.
注意:我确实稍微更改了 UpdateInstructorCourses 函数中显示的逻辑以满足我的需要。
回答by iuppiter
You can also call the tables independently
您也可以独立调用表
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();

