TransactionScope: предотвращение распределенных транзакций

У меня есть родительский объект (часть DAL), который содержит среди других вещей, набор (List<t>) из дочерних объектов.

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

Я собирался использовать стандартные Транзакции ADO, но на моих перемещениях, я споткнулся через объект TransactionScope, которому я верю, позволит мне перенести все взаимодействие DB в родительский метод (наряду со всем взаимодействием в дочернем методе) в одной транзакции.

Пока все хорошо..?

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

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

Больше в сущности сделайте меня...

  1. Создайте отдельные соединения в родителе и ребенке (хотя с той же строкой подключения)
  2. Создайте соединение в родителе передача это через в качестве параметра (кажется неуклюжим мне),
  3. Что-то еще...?

ОБНОВЛЕНИЕ:

В то время как кажется, что я был бы в порядке с помощью своего обычного.NET3.5 + и SQL Server 2008 +, другая часть этого проекта будет использовать Oracle (10 г), таким образом, я мог бы также практиковать технику, которая может последовательно использоваться через проекты.

Таким образом, я просто передам соединение до дочерних методов.


Опция 1 Пример кода:

using (TransactionScope ts = new TransactionScope())
            {
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.Connection = conn;
                        cmd.Connection.Open();
                        cmd.CommandType = CommandType.StoredProcedure;

                        try
                        {
                            //create & add parameters to command

                            //save parent object to DB
                            cmd.ExecuteNonQuery();

                            if ((int)cmd.Parameters["@Result"].Value != 0)
                            {
                                //not ok
                                //rollback transaction
                                ts.Dispose();
                                return false;
                            }
                            else //enquiry saved OK
                            {
                                if (update)
                                {
                                    enquiryID = (int)cmd.Parameters["@EnquiryID"].Value;
                                }

                                //Save Vehicles (child objects)
                                if (SaveVehiclesToEPE())
                                {
                                    ts.Complete();
                                    return true;
                                }
                                else
                                {
                                    ts.Dispose();
                                    return false;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            //log error
                            ts.Dispose();
                            throw;
                        }
                    }
                }
            }
19
задан John Foster 13 June 2011 в 10:53
поделиться

3 ответа

Многие поставщики ADO баз данных (например, Oracle ODP.NET) действительно начинают распределенные транзакции, когда вы используете TransactionScope для транзакций через несколько соединений - даже если они используют одно и то же соединение нить.

Некоторые провайдеры (например, SQL2008 в .NET 3.5+) распознают, когда создается новое соединение в области транзакции, которая ссылается на ту же строку соединения, и не приводит к работе DTC.Но любое отклонение в строке подключения (например, параметры настройки) может предотвратить это - и поведение вернется к использованию распределенной транзакции.

К сожалению, единственный надежный способ гарантировать, что ваши транзакции будут работать вместе без создания распределенной транзакции, - это передать объект подключения (или IDbTransaction ) методам, которым необходимо «продолжить» ту же транзакцию. .

Иногда помогает повысить уровень подключения к члену класса, в котором вы выполняете работу, но это может создать неловкие ситуации - и усложняет управление временем жизни и удалением объекта подключения (поскольку это обычно исключает использование с использованием оператора ).

24
ответ дан 30 November 2019 в 04:20
поделиться

Опытным путем я определил, что (для поставщика SQL Server) если процесс может использовать пул соединений для совместного использования соединения (и транзакции) между родительским и дочерним процессами, DTC не обязательно будет задействован.

Это большое «если», однако, согласно вашему примеру, соединение, созданное родительским процессом, не может использоваться дочерними процессами (вы не закрываете / не освобождаете соединение перед вызовом дочерних процессов). Это приведет к транзакции, охватывающей два фактических соединения, в результате чего транзакция будет переведена в распределенную транзакцию.

Кажется, что было бы легко реорганизовать ваш код, чтобы избежать этого сценария: просто закройте соединение, созданное родительским процессом, перед вызовом дочерних процессов.

2
ответ дан 30 November 2019 в 04:20
поделиться

В вашем примере TransactionScope все еще находится в контексте метода, вы можете просто создать SqlTransaction с несколькими командами под ним. Используйте TransactionScope, если вы хотите переместить транзакцию из метода, скажем, вызывающего объекта этого метода, или если вы обращаетесь к нескольким базам данных.

Обновление: неважно, я только что заметил звонок ребенка. В этой ситуации вы можете передать объект подключения дочерним классам. Кроме того, вам не нужно вручную удалять TransactionScope - использование блоков действует как блоки try-finally и будет выполнять удаление даже при исключениях.

Обновление 2: еще лучше передать IDbTransaction дочернему классу. Связь может быть получена из этого.

1
ответ дан 30 November 2019 в 04:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: