1

这是我在这里的第一篇文章,所以我希望一切都好。

这是我的问题:我的数据库中有一个名为UserTypes的表。它有:

  1. ID;
  2. 是私人的;
  3. 父ID;

相关的是第一个和第三个。我有另一个名为UserTypes_T的表,其中包含不同类型的信息,即特定于语言的信息。这些字段是:

  1. 语言_ID;
  2. 用户类型_ID;
  3. 姓名;

我想要实现的是从UserTypes表中加载整个层次结构并将其显示在 TreeView 中(这目前不相关)。然后,通过选择一些用户类型,我可以在单独的编辑框(名称)和组合框(父级)中编辑它们。

一切正常,直到我尝试将更改保留在数据库中。EF 为我生成了这些表的两个实体类:

用户类型的类有:

  1. ID;
  2. 是私人的;
  3. 父ID;
  4. 自引用的导航属性(0..1);
  5. 子元素的导航属性;
  6. UserTypes_T 表的另一个导航属性 (1..*);

翻译信息的类有:

  1. 用户类型_ID;
  2. 语言_ID;
  3. 姓名;
  4. UserTypes 表的导航属性 (*..1);
  5. 语言表的导航属性 (*..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 ......也许有办法阻止这种行为?

4

1 回答 1

1

大家好,我刚刚发现问题所在,如果其他人遇到同样的问题,我会发布答案。

问题是我在服务器上进行了一些验证,以查看用户类型之间是否没有循环引用。所以,我在服务器上的方法看起来像:

using (MyEntities context = new MyEntities()) 
{
    string errMsg = MyValidator.ValidateSomething(context.UserTypes,...);
    if (!string.IsNullOrEmpty(errMsg)) throw new FaultException(errMsg);
    //some other code here...
    context.UserTypes.ApplyChanges(_userType);//_userType is the one that is updated
    context.UserTypes.SaveChanges();
}

问题在于,在进行验证时,上下文已被填充,并且在尝试保存更改时,存在具有相同键值的对象。

解决方案很简单 - 使用不同的上下文来验证服务器上的内容:

using (MyEntities validationContext = new MyEntities())
{
    //validation goes here...
}
using (MyEntities context = new MyEntities())
{
    //saving changes and other processing...
}

另一个可以是:

using (MyEntities context = new MyEntities())
{
    using (MyEntities validationContext = new MyEntities())
    {
        //validation
    }
    //saving changes and other processing...
}

就是这样!我希望它对某人有用!

于 2011-06-09T06:29:13.143 回答