EF STE 和自引用表的问题
这是我在这里的第一篇文章,所以我希望一切都好。
这是我的问题: 我的数据库中有一个名为 UserTypes 的表。它有:
- ID;
- 是私人的;
- 家长ID;
相关的是第一和第三个。 我有另一个名为 UserTypes_T 的表,其中包含不同类型的信息,即特定于语言的信息。这些字段是:
- Language_ID;
- 用户类型_ID;
- 姓名;
我想要实现的是从 UserTypes 表加载整个层次结构并将其显示在 TreeView 中(目前不相关)。然后,通过选择一些用户类型,我可以在单独的编辑框(名称)和组合框(父级)中编辑它们。
一切正常,直到我尝试将更改保留在数据库中。 EF 为这些表生成了两个实体类:
用户类型的类有:
- ID;
- 是私人的;
- 父ID;
- 自引用的导航属性 (0..1);
- 子元素的导航属性;
- UserTypes_T 表的另一个导航属性 (1..*);
翻译信息的类有:
- UserType_ID;
- 语言_ID;
- 姓名;
- UserTypes 表的导航属性 (*..1);
- 语言表的导航属性 (*..1);
我使用以下方式获取所需的数据:
return context.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
在我的 WCF Web 服务中。我可以毫无问题地添加新的用户类型,但是当我尝试更新旧的用户类型时,会发生一些奇怪的事情。
如果我更新根元素(Parent_ID==null),一切正常! 如果我更新 Parent_ID!=null 的元素,则会收到以下错误:
AcceptChanges 无法继续,因为该对象的键值与 ObjectStateManager 中的另一个对象冲突。
我在互联网上搜索并阅读了 Diego B Vega (还有更多),但我的问题不同。当我更改父用户类型时,我实际上更改的是 Parent_ID 属性,而不是导航属性。我总是尝试使用 ID,而不是生成的导航属性,以避免出现问题。
我做了一些研究,试图看看我得到的对象图是什么,发现有很多重复的实体:
根元素有一个其子元素的列表。每个子元素都有一个对根或其父元素的反向引用,等等。你可以想象。由于我没有使用这些导航属性,因为我使用 ID 来获取/设置所需的数据,所以我从模型中删除了它们。具体来说,我从 UserTypes 实体类中删除了点 4 和 5。然后我有一个对象图,其中每个元素仅一次。我尝试了新的更新,但遇到了同样的问题:
根元素更新得很好,但是有一些父元素的元素抛出了相同的异常。
我看到 UserTypes_T 实体类中有一个导航属性,指向用户类型,所以我也删除了它。然后这个错误就消失了。对象图中的所有项目都是唯一的。但问题仍然存在 - 我可以毫无问题地更新我的根元素,但是当尝试更新子元素(没有排除)时,我在生成的 Model.Context.Extensions 类中遇到了空引用异常:
if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))
{
context.AddObject(entityInSet.Item1, entityInSet.Item2);//here!
}
我尝试仅更新名称(位于UserTypes_T中)但错误是相同的。
我已经没有想法了,我已经尝试解决这个问题 8 个小时了,所以如果有人给我想法或分享他们的经验,我将不胜感激。
PS:
我成功更新子对象的唯一方法是使用以下代码检索数据:
var userTypes = argoContext.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
foreach (UserType ut in userTypes)
{
ut.UserType1 = null;
ut.UserTypes1 = null;
}
return userTypes;
其中 UserType1 是导航属性,指向父用户类型和 UserTypes1是导航属性,保存子元素的列表。这里的问题是 EF“修复”对象并将 Parent_ID 更改为 null。如果我再次将其设置回来,EF 也会设置 UserTypes1...也许有办法阻止这种行为?
This is my first post here, so I hope everything is fine.
Here is my problem:
I have a table in my database called UserTypes. It has:
- ID;
- IsPrivate;
- Parent_ID;
The relevant ones are the first and the third one.
I have another table called UserTypes_T which has information for the different types, that is language specific. The fields are:
- Language_ID;
- UserType_ID;
- Name;
What I'm trying to achieve is load the entire hierarchy from the UserTypes table and show it in a TreeView (this is not relevant for now). Then, by selecting some of the user types I can edit them in separate edit box (the name) and a combo box (the parent).
Everything works fine until I try to persist the changes in the database. EF has generated for me two entity classes for those tables:
The class for the user types has:
- ID;
- IsPrivate;
- Parent_ID;
- A navigational property for the self-reference (0..1);
- A navigational property for the child elements;
- Another navigational property for the UserTypes_T table (1..*);
The class for the translated information has:
- UserType_ID;
- Language_ID;
- Name;
- A navigational property to the UserTypes table (*..1);
- A navigational property to the Languages table (*..1);
I get the data I need using:
return context.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
in my WCF Web service. I can add new user types with no problems, but when I try to update the old ones, some strange things happen.
If I update a root element (Parent_ID==null) everything works!
If I update an element where Parent_ID!=null I get the following error:
AcceptChanges cannot continue because the object’s key values conflict with another object in the ObjectStateManager.
I searched all over the internet and read the blog post from Diego B Vega (and many more) but my problem is different. When I change a parent user type, I actually change the Parent_ID property, not the navigational property. I always try to work with the IDs, not the generated navigational properties in order to avoid problems.
I did a little research, tried to see what is the object graph that I get and saw that there were lots of duplicate entities:
The root element had a list of its child elements. Each child element had a back reference to the root or to its parent and so on. You can imagine. As I wasn't using those navigational properties, because I used the IDs to get/set the data I needed, I deleted them from the model. To be specific I deleted points 4 and 5 from the UserTypes entity class. Then I had an object graph with each element only once. I tried a new update but I had the same problem:
The root element was updated fine, but the elements, that had some parents, threw the same exception.
I saw that I had a navigational property in the UserTypes_T entity class, pointing to a user type, so I deleted it too. Then this error disappeared. All the items in the object graph were unique. But the problem remained - I could update my root element with no problems, but when trying to update the children (with no exclusions) I got a null reference exception in the generated Model.Context.Extensions class:
if (!context.ObjectStateManager.TryGetObjectStateEntry(entityInSet.Item2, out entry))
{
context.AddObject(entityInSet.Item1, entityInSet.Item2);//here!
}
I tried to update only the name (which is in UserTypes_T) but the error is the same.
I'm out of ideas and I've been trying to solve this problem for 8 hours now, so I'll appreciate if someone gives me ideas or share their experience.
PS:
The only way I succeeded updating a child object was using the following code to retrieve the data:
var userTypes = argoContext.UserTypes.Include("UserTypes_T").Where(ut => ut.IsPrivate==false).ToList();
foreach (UserType ut in userTypes)
{
ut.UserType1 = null;
ut.UserTypes1 = null;
}
return userTypes;
where UserType1 is the navigational property, pointing to the parent user type and UserTypes1 is the navigational property, holding a list of the child element. The problem here was that EF "fixups" the objects and changes the Parent_ID to null. If I set it back again, EF sets the UserTypes1, too... Maybe there is a way to stop this behavior?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
好的大家,我刚刚找到问题所在,如果其他人遇到同样的问题,我会发布答案。
问题是我正在服务器上进行一些验证,以查看用户类型之间是否不存在循环引用。所以,我在服务器上的方法看起来像这样:
问题是,在进行验证时,上下文已填充,并且在尝试保存更改时,存在具有相同键值的对象。
解决方案很简单 - 使用不同的上下文来验证服务器上的内容:
另一个可以是:
就是这样!我希望它对某人有用!
OK everybody, I just found what the problem was and I'm posting the answer if anybody else encounters the same issue.
The problem was that I was making some validation on the server in order to see if there isn't a circular reference between the user types. So, my method on the server looked something like:
The problem is that when making the validation, the context is filled and when trying to save the changes, there are objects with the same key values.
The solution is simple - to use different context for validating things on the server:
Another one can be:
That's it! I hope it can be useful to somebody!