EF STE和自引用表的问题

时间:2011-06-08 15:11:05

标签: entity-framework hierarchy self-tracking-entities self-reference

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

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

  1. ID
  2. IsPrivate;
  3. PARENT_ID;
  4. 相关的是第一个和第三个。 我有另一个名为 UserTypes_T 的表,其中包含不同类型的信息,即特定于语言的信息。字段是:

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

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

      用户类型的类具有:

      1. ID
      2. IsPrivate;
      3. PARENT_ID;
      4. 自我引用的导航属性(0..1);
      5. 子元素的导航属性;
      6. UserTypes_T表的另一个导航属性(1 .. *);
      7. 翻译信息的类有:

        1. UserType_ID;
        2. LANGUAGE_ID;
        3. 名称;
        4. UserTypes表的导航属性(* .. 1);
        5. 语言表(* .. 1)的导航属性;
        6. 我得到了我需要的数据:

          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,而不是生成的导航属性,以避免出现问题。

          我做了一些研究,试图看看我得到的对象图是什么,看到有很多重复的实体:

          根元素有一个子元素列表。每个子元素都有一个对root或其父级的后引用,依此类推。你可以想象。因为我没有使用那些导航属性,因为我使用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 ...也许有办法阻止这种行为?

1 个答案:

答案 0 :(得分: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...
}

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