【问题标题】:Data Context's SubmitChanges method causing a entity reference to be set to null数据上下文的 SubmitChanges 方法导致实体引用设置为 null
【发布时间】:2011-04-06 09:08:00
【问题描述】:

我知道这看起来有点长,但我试图尽可能彻底地解释这个问题。

我们在 linq to sql 数据上下文类中遇到了一个非常“奇异”的问题。我们有一个这样的 n 层架构:我们有 3 个类 MotherClass、ChildClass、ChildChildrenClass

MotherClass 看起来像这样:

public class MotherClass
{
     private EntitySet<ChildClass> _Children;

     [Column]
     public int Id { get; set; }

     [Association(Storage = "_Children", ThisKey = "Id", OtherKey = "MotherId")]
     public EntitySet<ChildClass> Children
     {
           get { return _Children; }
           set { _Children= value; }
     }
}

而 ChildClass 看起来像:

public class ChildClass
{
     private EntityRef<MotherClass> _Mother;
     private EntitySet<ChildChildrenClass> _ChildChildren;

     [Column]
     public int Id { get; set; }

     [Column]
     public int MotherId { get; set; }

     [Association(Storage = "_Mother", IsForeignKey = true, ThisKey = "MotherId", OtherKey = "Id")]
     public MotherClass Mother
     {
           get { return _Mother.Entity; }
           set { _Mother.Entity = value; }
     }

     [Association(Storage = "_ChildChildren", ThisKey = "Id", OtherKey = "ChildId", DeleteRule = "NO ACTION")]
     public EntitySet<ChildChildrenClass> ChildChildren
     {
           get { return _ChildChildren; }
           set { _ChildChildren= value; }
     }
}

还有神奇地命名为 ChildChildrenClass 的第三个类:

public class ChildChildrenClass
    {
         private EntityRef<ChildClass> _Child;

         [Column]
         public int Id { get; set; }

         [Column]
         public int ChildId { get; set; }

         [Association(Storage = "_Child", IsForeignKey = true, ThisKey = "ChildId", OtherKey = "Id")]
         public ChildClass Child
         {
               get { return _Child.Entity; }
               set { _Child.Entity = value; }
         }
    }

当我们对 ChildClass 对象进行更新并删除一些与之关联的 ChildChildrenClass 项时,就会出现问题。代码如下所示:

DataContext dc = new DataContext(conStr);
dc.StartTransaction();//our custom method for handling transactions
ChildClass cclass = dc.ChildClass.GetById(id);//our method for getting the ChildClass from db
//... here we set some values we want to edit
//...
//...
dc.SubmitChanges(ConflictMode.FailOnFirstConflict);//these actions are cool
//after this the problems arise
 List<ChildChildrenClass> ccc = GetAllChildren();//method that gets all the childChildrenClass objects from db
foreach (ChildChildrenClass child in ccc)
{
     dc.GetTable(child.GetType()).DeleteOnSubmit(child);
}
dc.SubmitChanges(ConflictMode.FailOnFirstConflict);
//AFTER CALLING THIS METHOD THE PROBLEM APPEARS

上面提到的问题是cclass.Mother 属性被神奇地设置为null。经过大量调试(在 Mother set 方法中放置刹车点揭示了这一点),我们注意到在某些外部代码中的 SubmitChanges() 期间该属性被设置为 null。

SubmitChanges() 方法成功完成(ChildChildrenClass 项被删除),但这会导致在此之后运行的代码出现问题。我们使用相同的 DataContext(由于事务)并再次调用引发此异常的 SubmitChanges() 方法:

System.InvalidOperationException:尝试删除 MotherClass 和 ChildClass 之间的关系。但是,不能将关系的外键之一 (ChildClass.MotherId) 设置为 null。 在 System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.SynchDependentData() 在 System.Data.Linq.ChangeProcessor.ValidateAll(IEnumerable`1 列表) 在 System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode) 在 System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)

【问题讨论】:

    标签: c# .net linq-to-sql datacontext


    【解决方案1】:

    我很久以前写博客引擎的时候也遇到过这个问题。我从联结表中删除了几行后出现了问题。删除东西没问题,然后不管我做什么,下一个SubmitChanges()出现了这个确切的异常。

    在花了大约一天左右的时间解决问题后,我恢复了解决方法:

    • 创建 DataContext 的新实例
    • 从新的 DataContext 中获取任何正在使用的实体的新实例

    我知道这是非常 hacky,但这是我可以解决它的唯一方法。我看到您在那里使用交易,这会使这变得更难一些。也许尝试使用两个不同的事务(一个用于旧的 DataContext,一个用于新的),如果第二个失败则回滚第一个?
    我知道这很老套。

    也许尝试使用另一个没有问题的 ORM(例如 NHibernate)。

    【讨论】:

      【解决方案2】:

      DataContext 实例应该永远被重复使用。每次由于框架无法确保数据在SubmitChanges() 调用之间没有更改,建议的方法是在提交更改后处理 DataContext 并在另一个事务需要SubmitChanges() 时创建一个新的需要打电话。

      此外,DataContext 对象已经包装了事务中的所有插入、更改和/或删除。

      【讨论】:

      • "DataContext 实例永远不应该被重用。"你能发布一些参考来支持这个建议吗?
      • 阅读 Joseph C. Rattz, Jr 的“Pro LINQ (Language Integrated Query) in C# 2008”的第 503-509 页虽然没有明确说明,但结果集缓存不匹配和内置更改跟踪机制使得 DataContext 和数据库之间难以长时间保持一致性。
      【解决方案3】:

      不要使用 LinQ-To-SQL,因为

      1. 它已被 Microsoft 停产
      2. 这是一个很好的工具,但只适用于非常 简单的域。它有它的问题 与关系。
      3. 有很多选择。如果 你需要设计时支持选择 ADO.NET 实体框架。如果你 有一个复杂的域来映射尝试 NHibernate,它还具有 大执着无知。 亚音速的粉丝也不少 在那里。

      【讨论】:

      • “被微软停产”的传言不属实。这是由于对他们所说的实体框架是他们在该领域的主要工具的误解(或据我所知是故意的误读)。它没有停产,并且自假定的停产以来得到了进一步的发展。
      • 我不知道 LinqToSql 有这样的问题,我们一直在将它用于更简单的项目而没有很多问题,但是出现了这样的问题并且浪费了大量时间寻找大量的代码一些可能的映射错误开始让我生气。也许我们应该考虑迁移到其他 ORM 而不是应用 hacks
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多