Можно ли включить изменения linq-to-sql и обновления адаптера таблицы набора данных ADO.NET в одну транзакцию? - PullRequest
0 голосов
/ 20 мая 2009

Вот соответствующие технологии, с которыми я работаю:

  • Devart dot Connect для Oracle (для облегчения Linq-to-Sql для Oracle).
  • Сильно типизированные наборы данных ADO.NET.
  • База данных Oracle.

Вот вызов:

  • Мой устаревший код отправляет обновления базы данных с наборами данных ADO.NET и адаптерами таблиц.
  • Я бы хотел начать преобразование этого кода в Linq-to-Sql, но я бы хотел сделать это по частям, чтобы минимизировать отток и риск кода.

Вот мое подтверждение концепции схемы:

Родительская таблица

  • Parent.Id
  • Parent.Name

Детский стол

  • Child.Id
  • Child.ParentId
  • Child.Name

Вот мое доказательство концепции кодового блока:

using System;
using System.Data.Common;
using DevArtTry1.DataSet1TableAdapters;

namespace DevArtTry1
{
    class Program
    {
        static void Main(string[] args)
        {
            using (DataContext1 dc = new DataContext1())
            {
                dc.Connection.Open();
                using (DbTransaction transaction = dc.Connection.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
                {
                    dc.Transaction = transaction;

                    Parent parent = new Parent();
                    parent.Id = 1;
                    parent.Name = "Parent 1";
                    dc.Parents.InsertOnSubmit(parent);
                    dc.SubmitChanges(); // By virtue of the Parent.Id -> Child.ParentId (M:N) foreign key, this statement will impose a write lock on the child table.

                    DataSet1.CHILDDataTable dt = new DataSet1.CHILDDataTable();
                    DataSet1.CHILDRow row = dt.NewCHILDRow();
                    row.ID = 1;
                    row.PARENTID = 1;
                    row.NAME = "Child 1";
                    dt.AddCHILDRow(row);

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                     // cta.Transaction = transaction;  Not allowed because you can't convert source type 'System.Data.Common.DbTransaction to target type 'System.Data.OracleClient.OracleTransaction.
                    cta.Update(dt); // The thread will encounter a deadlock here, waiting for a write lock on the Child table.
                    transaction.Commit();
                }
            }

            Console.WriteLine("Successfully inserted parent and child rows.");
            Console.ReadLine();
        }
    }
}

  • Как показывают вышеприведенные комментарии, поток будет бесконечно останавливаться при вызове обновления дочернего адаптера данных, потому что он будет бесконечно ждать блокировки записи в дочерней таблице. [Обратите внимание на отношение внешнего ключа: Parent.Id -> Child.ParentId (M: N)]

Вот мой вопрос:

  • Я хочу обернуть весь блок кода в транзакции.
  • Могу ли я сделать это? Учитывая, что:
    • Я хочу зафиксировать обновление в родительской таблице с Метод Linq-to-Sql SubmitChanges ...
    • И я хочу совершить обновление таблицы Child с Набор данных ADO.NET настольный адаптер .

Вот две интересные сноски:

  1. Все это работает в задний ход. То есть если бы я хотел отправить изменения в родительскую таблицу с адаптером данных и изменениями дочерний стол с linq-to-sql ... что будет работать .
  2. Я пытался явно присоединить транзакцию к адаптеру данных, но компилятор не разрешил это, потому что это другой тип транзакции.

                    CHILDTableAdapter cta = new CHILDTableAdapter();
                cta.Transaction = transaction; // Not allowed because you can't convert source type 'System.Data.Common.DbTransaction' to target type 'System.Data.OracleClient.OracleTransaction'.
                cta.Update(dt);
                transaction.Commit();
    

Ответы [ 3 ]

1 голос
/ 31 июля 2010

У меня была такая же проблема, возникли следующие две ошибки:

  • нарушение ограничения целостности (ORA-02291)
  • "Невозможно вставить объект с тем же ключом, если ключ не создан базой данных"

Проблема заключалась в том, что столбец идентификации дочернего объекта не был установлен правильно. Если DotConnect LINQ не принимает идентификационный ключ, то свойства объектов, по-видимому, устанавливаются нерегулярно, что приводит к непоследовательным обновлениям, приводящим к нарушениям целостности.

Вот исправление:

  • LINQ необходимо знать, что первичный ключ дочернего элемента является ключом сущности и генерируется автоматически.
  • В Oracle установите автоинкрементный ключ для дочернего объекта.
  • Сначала создайте последовательность:

      DROP SEQUENCE MyChild_SEQ;
      CREATE SEQUENCE MyChild_SEQ
          MINVALUE 1
          MAXVALUE 999999999999999999999999999
          START WITH 1
          INCREMENT BY 1
          CACHE 20;
    
  • Далее создайте триггер OnInsert:

    CREATE OR REPLACE TRIGGER MyChild_AUTOINC 
    BEFORE INSERT
    ON MyChildObject
    FOR EACH ROW
    BEGIN
      SELECT MyChild_SEQ.nextval
      INTO :NEW.MyChild_ID
      FROM dual;
    END MyChild_AUTOINC ; 
    ALTER TRIGGER MyChild_AUTOINC ENABLE
    
  • Измените модель хранилища для включения нового автоматически сгенерированного первичного ключа:

    • В EntityDeveloper для dotConnect откройте модель хранения LINQ (файл .LQML).
    • Установите для ключа объекта дочернего объекта значение «Auto Generated Value», а для автоматической синхронизации - «OnInsert».
    • Сохраните модель хранения, а в Visual Studio очистите и перестройте решение.
    • Удалите любой код, который явно устанавливает первичный ключ дочернего элемента.
      • LINQ неявно распознает это как автоинкремент и извлекает идентификатор, созданный триггером.
  • В коде после создания дочернего объекта присоедините его к родительскому объекту, как показано ниже:

    ChildType newChild = new ChildType();
    DataContext.InsertOnSubmit(newChild);
    Parent.Child = newChild;
    

Вот дополнительные ресурсы:

Ура!

1 голос
/ 20 мая 2009

Я ничего не знаю о транзакциях Oracle ... но со стороны dotnet у вас должно быть все в порядке, чтобы контролировать транзакцию самостоятельно. Убедитесь, что обе технологии используют один и тот же экземпляр подключения.

Когда мы контролируем транзакции через соединение, а не через ORM, мы используем объем транзакции: http://msdn.microsoft.com/en-us/library/ms172152.aspx

0 голосов
/ 20 мая 2009

Используйте класс TransactionScope.

Помните, что если вы используете разные базы данных (или они находятся на разных серверах), вам необходимо проверить конфигурацию DTC.

...