Метод SubmitChanges контекста данных, заставляющий ссылку на объект быть установленным в нуль - PullRequest
6 голосов
/ 31 августа 2010

Я знаю, это выглядит немного длинно, но я пытался объяснить проблему как можно более подробно.

У нас очень экзотическая проблема с классом контекста данных 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. После большой отладки (размещение точек торможения в методе «Мать» показало это), мы заметили, что во время SubmitChanges () в некотором внешнем коде для свойства устанавливается ноль.

Метод SubmitChanges () завершается успешно (элементы ChildChildrenClass удаляются), но это вызывает проблему с кодом, который выполняется после этого. Мы используем тот же DataContext (из-за транзакции) и снова вызываем метод SubmitChanges (), который выдает это исключение:

System.InvalidOperationException: была предпринята попытка удалить связь между MotherClass и ChildClass. Однако один из внешних ключей отношения (ChildClass.MotherId) не может быть установлен в нуль. в System.Data.Linq.ChangeTracker.StandardChangeTracker.StandardTrackedObject.SynchDependentData () в System.Data.Linq.ChangeProcessor.ValidateAll (список IEnumerable`1) в System.Data.Linq.ChangeProcessor.SubmitChanges (ConflictMode faultMode) в System.Data.Linq.DataContext.SubmitChanges (ConflictMode faultMode)

Ответы [ 3 ]

2 голосов
/ 31 августа 2010

Я также сталкивался с этой проблемой давным-давно, когда писал блог-движок. Проблема возникла после того, как я удалил пару строк из соединительной таблицы. Удаление все прошло нормально, тогда, что бы я ни делал, это точное исключение появилось на следующем SubmitChanges().

Потратив примерно один день на решение проблемы, я прибег к решению проблемы:

  • Создать новый экземпляр DataContext
  • Получить новый экземпляр любых сущностей, используемых в новом DataContext

Я знаю, что это очень хакерский, но это был единственный способ, которым я мог решить это. Я вижу, вы используете транзакцию там, что сделало бы это немного сложнее. Возможно, попробуйте использовать две разные транзакции (одну для старого DataContext и одну для новой) и откатить первую, если вторая не удалась?
Я знаю, что это хаки.

Возможно, попробуйте использовать другой ORM (например, NHibernate), у которого нет проблем с этим.

1 голос
/ 16 сентября 2010

DataContext экземпляры должны никогда использоваться повторно.Каждый раз, поскольку платформа не может гарантировать, что данные не были изменены между вызовами SubmitChanges(), рекомендуемый подход состоит в том, чтобы избавиться от DataContext после отправки изменений и создать новый, если другая транзакция требует SubmitChanges()требуется вызов.

Кроме того, объект DataContext уже включает любые вставки, изменения и / или удаления в транзакции.

1 голос
/ 06 сентября 2010

Не используйте LinQ-To-SQL, потому что

  1. Microsoft больше не выпускает
  2. Это хороший инструмент, но только для очень простых доменов.У него есть проблемы с отношениями.
  3. Есть много альтернатив.Если вам нужна поддержка времени разработки, выберите ADO.NET Entity Framework.Если у вас сложный домен для сопоставления, попробуйте NHibernate, который также отличается большим невежеством.Там также много поклонников дозвукового.
...