数据上下文的SubmitChanges方法导致实体引用被设置为null。

7

我知道这看起来有点冗长,但我尽可能详细地解释了问题。

我们在使用linq to sql数据上下文类时遇到了一个非常“奇特”的问题。我们有一个三层架构结构,如下所示:我们有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。 at System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.SynchDependentData() at System.Data.Linq.ChangeProcessor.ValidateAll(IEnumerable`1 list) at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode) at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
3个回答

2

DataContext实例永远不应该被重用。因为框架无法确保数据在SubmitChanges()调用之间未被更改,所以建议的方法是在提交更改后处理掉DataContext并创建一个新的如果需要另一个需要SubmitChanges()调用的事务。

此外,DataContext对象已经将任何插入、更改和/或删除包装在事务中。


"DataContext实例不应该被重复使用。" 你能提供一些支持这个建议的参考资料吗? - greenoldman
请阅读Joseph C. Rattz Jr.的《Pro LINQ(C# 2008中的语言集成查询)》第503-509页。虽然没有明确说明,但结果集缓存不匹配和内置更改跟踪机制使得在DataContext和数据库之间长时间保持一致性变得困难。 - Neil T.

2
很久以前,我写博客引擎时也遇到了这个问题。在删除连接表中的几行后,问题就出现了。虽然删除操作进行得很顺利,但无论我做什么,在下一次SubmitChanges()时都会出现这个异常。
花费了大约一天的时间来解决这个问题后,我使用了一个变通方法:
  • 创建DataContext的新实例
  • 从新的DataContext中获取正在使用的任何实体的新实例
我知道这种方法非常hacky,但这是我能解决它的唯一方法。我看到你在使用事务,这会让它变得有点困难。也许尝试使用两个不同的事务(一个用于旧的DataContext,另一个用于新的),如果第二个失败,则回滚第一个? 我知道这很hacky。
也许尝试使用另一个ORM(例如NHibernate),它没有这个问题。

1

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

  1. 它已被微软停止支持
  2. 它是一个很好的工具,但只适用于非常简单的领域。它在关系方面存在问题。
  3. 有很多替代品。如果需要设计时支持,请选择ADO.NET实体框架。如果您有一个复杂的领域需要映射,请尝试NHibernate,它还具有很好的持久性忽略功能。还有很多Subsonic的粉丝。

4
“被微软停用”的谣言并不是真的。这是由于误解(或者据我所知故意曲解)他们说实体框架是在这个领域中他们的主要工具。它并没有被停用,而且自所谓的停用以来还有进一步的开发。 - Jon Hanna
我不知道LinqToSql有这样的问题,我们一直在较简单的项目中使用它而没有太多问题,但是出现这样的问题并且花费大量时间查找可能的映射错误开始让我感到恼火。也许我们应该考虑迁移到其他ORM而不是应用hack。 - Atzoya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接