问题描述
假设您的实体中有这些类.
Let's say you have these classes in your entities.
public class Parent
{
public int ParentID { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
public int ChildID { get; set; }
public int ParentID { get; set; }
public virtual Parent Parent { get; set; }
}
你有一个用户界面来更新 Parent
及其 Children
,这意味着如果用户添加新的 Child
那么你必须插入,如果用户编辑现有的 Child
则需要更新,如果用户删除 Child
则必须删除.现在显然如果您使用以下代码
And you have a user interface to update the Parent
along with its Children
, meaning if the user add new Child
then you have to insert, if the user edits an existing Child
then you need to update, and if the user removes a Child
then you have to delete. Now obviously if you use the following code
public void Update(Parent obj)
{
_parent.Attach(obj);
_dbContext.Entry(obj).State = EntityState.Modified;
_dbContext.SaveChanges();
}
它将无法检测到 Child
内的更改,因为 EF 无法检测到导航属性内的更改.
it won't be able to detect the changes inside the Child
because EF cannot detect changes inside a Navigation Property.
我已经问了这个问题 4 次了,得到的答案好坏参半.那么实际上是否可以在不变得复杂的情况下做这些事情呢?这个问题可以通过分离 Parent
和 Child
之间的用户界面来解决问题,但我不想这样做,因为合并了 Child
和 Parent
在一个菜单中在业务应用程序开发中非常常见,并且更加用户友好.
I've been asking this question for like 4 times and get mixed answers. So is it actually possible to do this stuff without it getting complicated? This problem can fix the problem by separating the user interface between Parent
and Child
but I don't want to because merging both Child
and Parent
in one menu is pretty common in business application development and more user friendly.
更新:我正在尝试下面的解决方案,但它不起作用.
public ActionResult(ParentViewModel model)
{
var parentFromDB = context.Parent.Get(model.ParentID);
if (parentFromDB != null)
{
parentFromDB.Childs = model.Childs;
}
context.SaveChanges();
}
EF 无法检测孩子内部的变化,而是无法判断如何处理老孩子.例如,如果 parentFromDB
第一次从数据库中拉出它时有 3 个孩子,那么我会删除第二个和第三个孩子.然后我得到 The relationship could not be changed because a or more of the foreign-key properties are non-nullable
保存时.
Instead of detecting changes inside the Children, EF won't be able to tell what to do with old child. For example if parentFromDB
has 3 children the first time I pull it from DB then I delete the 2nd and 3rd child. Then I'm getting The relationship could not be changed because one or more of the foreign-key properties is non-nullable
when saving.
我相信这就是发生的事情:无法更改关系,因为一个或多个外键属性不可为空
I believe this is what happened : The relationship could not be changed because one or more of the foreign-key properties is non-nullable
这让我回到了第一阶段,因为在我的场景中,我不能只从数据库中获取并更新条目并调用 SaveChanges
.
Which took me back to square one because in my scenario, I can't just fetch from the DB and update the entry and call SaveChanges
.
推荐答案
因为 EF 无法检测到导航属性内部的更改
because EF cannot detect changes inside Navigation Property
这似乎有点歪曲了对 _dbContext.Entry(obj).State = EntityState.Modified
没有将导航属性标记为已修改这一事实的描述.
This seems to be a somewhat distorted description of the fact that _dbContext.Entry(obj).State = EntityState.Modified
doesn't mark navigaton properties as modified.
当然,EF 会跟踪导航属性的变化.它跟踪附加到上下文的所有实体的属性和关联的变化.因此,您的问题的答案,现在是肯定的……
Of course EF tracks changes in navigation properties. It tracks changes in properties and associations of all entities that are attached to a context. Therefore, the answer to your question, now positively stated...
是否可以开箱即用地更新 EF 中的子集合
Is it possible to update child collection in EF out of the box
... 是:是.
唯一的事情是:你不这样做开箱即用.
The only thing is: you don't do it out of the box.
更新任何实体的开箱即用"方式是:
The "out of the box" way to update any entity, whether it be a parent or a child in some collection is:
- 从数据库中获取实体.
- 修改它们的属性或在它们的集合中添加/删除元素
- 调用
SaveChanges()
.
就是这样.Ef 跟踪更改,您从不明确设置实体 State
.
That's all. Ef tracks the changes and you never set entity State
s explicitly.
但是,在断开连接(n 层)的情况下,这会变得更加复杂.我们序列化和反序列化实体,因此不能有任何上下文来跟踪它们的变化.如果我们想将实体存储在数据库中,现在我们的任务是让 EF 知道更改.基本上有两种方法可以做到这一点:
However, in a disconnected (n-tier) scenario, this gets more complicated. We serialize and deserialize entities, so there can't be any context that tracks their changes. If we want to store the entities in the database, now it's our task to make EF know the changes. There are basically two ways to do this:
- 根据我们对实体的了解手动设置状态(例如:主键 > 0 表示它们存在并且应该更新)
- 绘制状态:从数据库中检索实体并将反序列化实体的更改重新应用到它们.
- Set the states manually, based on what we know about the entities (like: a primary key > 0 means that they exist and should be updated)
- Paint the state: retrieve the entities from the database and re-apply the changes from the deserialized entities to them.
当涉及到关联时,我们总是要绘制状态.我们必须从数据库中获取当前实体并确定添加/删除了哪些子实体.无法从反序列化的对象图本身推断出这一点.
When it comes to associations, we always have to paint the state. We have to get the current entities from the database and determine which children were added/deleted. There's no way to infer this from the deserialized object graph itself.
有多种方法可以缓解绘制状态这一无聊而复杂的任务,但这超出了本问答的范围.一些参考资料:
There various ways to alleviate this boring and elaborate task of painting the state, but that's beyond the scope of this Q&A. Some references:
- 用于更新整个聚合的通用存储库
- GraphDiff
这篇关于开箱即用地更新 EF 中的子集合真的不可能吗(又名非 hacky 方式)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!