实体框架:对具有复杂关系的对象进行 CRUD
我的系统中有许多对象,它们都继承自基类 Entity。这些对象有相当多的关系,包括一对一、一对多和多对多。由于我使用的是 WCF,因此我的 DbContext 对象与每个 CRUD 调用都断开连接。这导致我在对具有关系的对象进行基本 CRUD 操作时遇到了一些问题。
例如,我有一个具有基本父子关系的对象。我将其称为节点。
[DataContract(IsReference=true)]
public partial class Node : Entity
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public long? ParentID { get; set; }
[DataMember]
public List<Node> Children { get; set; }
}
我希望能够添加一个具有已存在的子节点的新节点,并添加一个具有尚不存在的子节点的节点。
// Node with a new child node
Node nodeWithNewChild = new Node()
{
Children = new List<Node>()
{
new Node()
}
}
// A pre-existing child node
Node existingChildNode = new Node();
// Node with a pre-existing child node
Node nodeWithExistingChild = new Node()
{
Children = new List<Node>()
{
existingChildNode
}
}
问题是,无论我如何处理,实体框架都会感到困惑。
当我使用基本的 DbContext.Nodes.Add
操作时,它会用现有的子项扰乱我的测试用例。它会在数据库中复制现有子项的条目,然后为这个新子项提供正确的 ParentID
。如果我循环子级并首先在子级上使用 DbContext.Nodes.Add ,也会发生这种情况。
我尝试循环遍历所有子级并在所有子级上使用 DbContext.Nodes.Attach
,然后在父级上使用 DbContext.Nodes.Add
,但这会导致异常我对一个新孩子的测试用例。
System.Data.Entity.Infrastruct.DbUpdateConcurrencyException:存储 更新、插入或删除语句影响了意外数量的 行 (0)。实体可能已被修改或删除 已加载。刷新 ObjectStateManager 条目。 ...
更不用说我担心这将如何工作,例如,如果您添加了一个带有孩子的节点,还有一个孩子,等等。我希望我的 CRUD 方法能够对所有可能的有效对象构造做出适当的反应。
从我能找到的研究来看,归根结底,EF 不适合或者不擅长这种事情,最好自己管理关系。这是真的吗?如果是这样,有我可以效仿的例子吗?有什么技巧之类的吗?
我开始使用一种使用反射来自己处理关系的方法,但我觉得这只是解决基本问题的一种荒谬的方法。
I have many objects in my system that all inherit from a base class, Entity. These objects have quite a few relationships, including one-to-one, one-to-many, and many-to-many. Since I am using WCF, my DbContext object is disconnected with every CRUD call. This has caused me to run into some issues with basic CRUD operations on objects with relationships.
For example, I have an object with a basic parent-child relationship. I will call it Node.
[DataContract(IsReference=true)]
public partial class Node : Entity
{
[DataMember]
public long ID { get; private set; }
[DataMember]
public long? ParentID { get; set; }
[DataMember]
public List<Node> Children { get; set; }
}
I want to be able to add a new node that has a child that already exists and also to add a node that has a child that does not exist yet.
// Node with a new child node
Node nodeWithNewChild = new Node()
{
Children = new List<Node>()
{
new Node()
}
}
// A pre-existing child node
Node existingChildNode = new Node();
// Node with a pre-existing child node
Node nodeWithExistingChild = new Node()
{
Children = new List<Node>()
{
existingChildNode
}
}
The problem is that no matter how I go about it, Entity Framework gets confused.
When I use a basic DbContext.Nodes.Add
operation, it messes up on my test case with an existing child. It makes a duplicate entry of the existing child in the database, and then gives this new child the correct ParentID
. This also happens if I loop through the children and use DbContext.Nodes.Add
on the children first.
I tried looping through all the children and using DbContext.Nodes.Attach
on all the children and then using DbContext.Nodes.Add
on the parent, but this causes an exception on my test case with a new child.
System.Data.Entity.Infrastructure.DbUpdateConcurrencyException: Store
update, insert, or delete statement affected an unexpected number of
rows (0). Entities may have been modified or deleted since entities
were loaded. Refresh ObjectStateManager entries. ...
Not to mention I am worried about how this would work if, for example, you added a node with a child with a child with a child and so on. I want my CRUD methods to react appropriately to all possible valid object constructions.
From the research I have been able to find, it comes to down to the fact that EF is just not meant for or is just bad at this kind of thing, and it is best to manage relationships myself. Is this true? If so, is there an example I can follow? Any tips, etc.?
I started on a method that would handle relationships myself that uses reflection, but I feel like this is just a ridiculous way to solve what should be a basic problem.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您必须附加现有子节点,而不附加新子节点。假设您可以通过查看 ID 来区分新节点和现有节点(ID > 0 表示:现有),则可能如下所示:
如果 ID == 0,则
childNode< /code> 也将被插入到数据库中。否则(由于附加到上下文)将不会创建新的子节点记录。
编辑:
如果您有一个由子代和孙代等组成的复杂图表,其中可以包含现有节点和新节点的混合,您可以将上面代码中的前两行替换为以下内容:
并添加以下方法:
我相信调用
Attach
和Add
在这里很重要(与上面的简单示例相反),因为当您将一个节点附加到上下文时,它的所有子节点和孙子也有依恋。 (这意味着它们现在处于Unchanged
状态,EF 会将节点下面的整个子图视为现有对象。)因此,您必须显式地将状态设置为Added
(通过调用Add
)当循环到达树中的下一级并且有一个新的子节点(ID == 0)时。这同样适用于调用Add
,因此如果子节点存在,则必须再次将子节点的状态重置为Unchanged
。编辑2:
也许在循环中首先调用
AttachOrAddChildren
更聪明:这样树将从叶子到根遍历,并且 EF 不必更改 的状态已经存在于上下文中的对象。这可能会更好的性能,但我不确定。也许无论哪条路都不重要。
You must attach for existing child nodes and not attach for new child nodes. Let's say you could distinguish between new and existing nodes by looking at their
ID
(ID > 0 means: existing) then this could look like this:In case ID == 0 the
childNode
will also be inserted into the DB. Otherwise (due to attaching to the context) no new child node record will be created.Edit:
If you have a complex graph of children and grandchildren and so on which can contain a mix of existing and new nodes you could replace the two first lines in the code above by the following:
And add the following method:
I believe that calling both
Attach
andAdd
is important here (in contrast to the simple example above) because when you attach a node to the context all its children and grandchildren get attached as well. (That means they are inUnchanged
state now and EF would consider the whole subgraph below the node as existing objects.) Therefore you must set the state toAdded
explicitely (by callingAdd
) when the loop reaches the next level in the tree and when you have a new child node (ID == 0). The same applies to callingAdd
, so that you must reset the state of child nodes toUnchanged
again if the nodes are existing.Edit 2:
Perhaps calling
AttachOrAddChildren
first in the loop is smarter:This way the tree would be traversed from the leaves to the root and EF would not have to change the state of objects which are already in the context. It might be better performance-wise but I am not sure. Possibly it doesn't matter which way around.