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

发布于 2024-09-16 21:46:26 字数 3396 浏览 5 评论 0原文

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

我们在 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(冲突模式失败模式) 在 System.Data.Linq.DataContext.SubmitChanges(冲突模式失败模式)

I know this looks a bit long but i tried to explain the problem as throughly as i could.

We are having a very 'exotic' problem with the linq to sql data context class. We have a n-tiered architectured structured like this: We have 3 classes MotherClass, ChildClass, ChildChildrenClass

MotherClass looks something like this:

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; }
     }
}

And ChildClass looks something like:

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; }
     }
}

And the third class that is magically named 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; }
         }
    }

The problem arises when we do an update on a ChildClass object and delete some of the ChildChildrenClass items that are associated with it.The code looks something like this:

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

The problem mentioned above is that the cclass.Mother property is magically set to null. After a lot of debugging (placing brakepoints in the Mother set method revealed this) we noticed that the property is being set to null during SubmitChanges() in some external code.

The SubmitChanges() method completes successfully (the ChildChildrenClass items are deleted) but this causes a problem with the code that runs after this. We are using the same DataContext (because of the transaction) and calling again the SubmitChanges() method that throws this exception:

System.InvalidOperationException: An attempt was made to remove a relationship between a MotherClass and a ChildClass. However, one of the relationship's foreign keys (ChildClass.MotherId) cannot be set to 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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独自←快乐 2024-09-23 21:46:26

我很早以前写博客引擎时也遇到过这个问题。当我从连接表中删除几行后,问题就出现了。删除内容一切顺利,然后无论我做什么,下一个 SubmitChanges() 都会出现这个确切的异常。

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

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

我知道这是非常 hacky ,但这是我解决这个问题的唯一方法。我看到你在那里使用交易,这会让事情变得有点困难。也许尝试使用两个不同的事务(一个用于旧的 DataContext,一个用于新的),并在第二个失败时回滚第一个事务?
我知道这很糟糕。

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

I have also encountered this issue long time ago when I wrote a blog engine. The problem arised after I deleted a couple of rows from a junction table. Deleting the things went okay, then no matter what I did, this exact exception came up on the next SubmitChanges().

After spending about a day or so solving the problem, I restorted to a workaround:

  • Create a new instance of the DataContext
  • Get a new instance of any entities in use from the new DataContext

I know this is very hacky, but this was the only way I could solve it. I see you use a transaction there, that would make this a little bit harder. Perhaps try to use two different transactions (one for the old DataContext, and one for the new), and roll back the first one if the second one fails?
I know this is hacky.

Perhaps try to use another ORM (eg. NHibernate) which doesn't have issues with this.

盗心人 2024-09-23 21:46:26

DataContext 实例应该永远被重用。每次 由于框架无法确保在 SubmitChanges() 调用之间数据没有发生更改,因此建议的方法是在提交更改后处理 DataContext,并在以下情况下创建一个新的 DataContext:需要另一个需要 SubmitChanges() 调用的事务。

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

DataContext instances should never be reused. Every time Because the framework has no way to ensure that the data has not been changed between SubmitChanges() calls, the recommended approach is to dispose of the DataContext after changes have been submitted and create a new one if another transaction requiring a SubmitChanges() call is needed.

In addition, the DataContext object already wraps any inserts, changes, and/or deletes in a transaction.

梦魇绽荼蘼 2024-09-23 21:46:26

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

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

Don't use LinQ-To-SQL, because

  1. It's discontinued by Microsoft
  2. It's a good tool, but only for very
    simple domains. It has its problems
    with relations.
  3. There are plenty of alternatives. If
    you need Design-Time Support choose
    ADO.NET Entity Framework. If you
    have a complex domain to map try
    NHibernate, which also features
    great persistence ignorance.
    There're also many fans of subsonic
    out there.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文