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

StackOverflow https://stackoverflow.com/questions/888299

Вопрос

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

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

Вот задача:

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

Вот мое доказательство концептуальной схемы:

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

  • Родитель.Идентификатор
  • Родитель.Имя

Детский стол

  • Child.Id
  • Child.ParentId
  • Ребенок.Имя

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

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's Метод SubmitChanges...
    • И я хочу сделать обновленную информацию о детской таблице с набором данных 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();
    
Это было полезно?

Решение

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

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

Другие советы

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

  • нарушение ограничения целостности (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).
    • Установите для ключа сущности дочернего объекта значение «Автосгенерированное значение», а для параметра «Автосинхронизация» — «OnInsert».
    • Сохраните модель хранилища, а затем в Visual Studio очистите и перестройте решение.
    • Удалите любой код, который явно устанавливает первичный ключ дочернего элемента.
      • LINQ неявно распознает это как автоматически увеличивающееся и получит идентификатор, созданный триггером.
  • В коде после создания дочернего объекта прикрепите его к родительскому, как показано ниже:

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

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

Ваше здоровье!

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top