SqlConnection и избежать продвижения в MSDTC

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

  • для запроса у нас есть статический заводской класс с методом CreateOpenConnection не более new SqlConnection(myConnectionString) и звонки Open() на нем. Этот метод вызывается перед выполнением запроса, и соединение удаляется после возвращения запроса.
  • для вставок / обновлений / удалений мы используем шаблон единицы работы, где изменения сгруппированы и представлены в база данных с вызовом work.Commit() вот так:

работа.Commit:

using (var tranScope = new TransactionScope(TransactionScopeOption.RequiresNew))
{
    using (var conn = DapperFactory.CreateOpenConnection())
    {
      var count = _changeTracker.CommitChanges(conn);

      tranScope.Complete();

      return count;
    }
}

это, кажется, отлично подходит для общего использования в рамках веб-службы, но в настоящее время дает мне msdtc проблемы, когда я пытаюсь использовать это в сочетании с ребусом.

из того, что я могу сказать, ребус (когда он обрабатывает сообщения в очереди) создает новый TransactionScope так что в случае сбоя в обработке сообщения материал можно откатить. Теперь это само по себе сработало отлично пока что. Я могу открыть новый SqlConnection внутри обработчика сообщений ребуса без каких-либо проблем (однако, используя наши устаревшие запросы Entity Framework и ручные SqlConnections внутри того же ребуса TransactionScope не работает,но я не считаю это проблемой прямо сейчас). Но вчера я задал следующий вопрос:--24-->

последовательная обработка определенного типа сообщений в ребус

на который ответ, похоже, заключается в использовании функции saga ребуса. Я попробовал реализовать это и настроил его так, чтобы сага ребуса сохранялась в новой базе данных SQL Server (с отдельной строкой подключения). По-видимому, используя то, что SQL-сервер настойчивость открывает SqlConnection самостоятельно, потому что в любое время я пытаюсь создать SqlConnection теперь, я получаю следующее исключение:

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

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

я предполагаю, что здесь происходит то, что ребус создает ambient TransactionScope и SqlConnection он создает, привлекает в эту область. И когда я пытаюсь создать свой собственный SqlConnection он также пытается заручиться эта окружающая область и поскольку задействовано несколько соединений, она повышается до MSDTC, который терпит неудачу.

  • добавить Enlist=false в строку подключения моего приложения, чтобы она никогда не подключалась к окружающим транзакциям.
  • изменить Commit метод, чтобы он не создавал новый TransactionScope (на которое мое соединение больше не будет подписываться потому что я просто сказал, что это не должно), но что он использует conn.BeginTransaction.

вот так:

var transaction = conn.BeginTransaction();

try
{
  var count = _changeTracker.CommitChanges(conn);
  transaction.Commit();
  return count;
}
catch
{
  transaction.Rollback();
  throw;
}
finally
{
  transaction.Dispose();
}

я просто не уверен, если это правильный подход и каковы возможные недостатки.

какие-либо советы?

обновление: чтобы уточнить, это не work.Commit() это давало мне проблемы, я совершенно уверен, что это сработает, но я никогда не доберусь туда, потому что мой запрос это то, что не получается.

An пример того, что не удается:

public int? GetWarehouseID(int appID)
{
  var query = @"
select top 1 ID from OrganizationUnits o
where TypeID & 16 = 16 /* warehouse */";

  using (var conn = _dapper.OpenConnection())
  {
    var id = conn.Query<int?>(query).FirstOrDefault();

    return id;
  }
}

это называется, когда TransactionScope был создан ребус, а также после SqlConnection открывается ребус. При открытии мой SqlConnection, он пытается заручиться и аварий

2 ответов


Я несколько удивлен, что вы это видите, потому что RequiresNew должны означает, что он изолирован от другой транзакции; обычно это сообщение означает, что соединение 2 было активировано внутри области транзакции-вы обязательно нет другого кода, создающего / открывающего соединение внутри этого блока?

ваше предлагаемое решение должно работать-хотя в некотором роде TransactionScopeOption.Suppress может быть удобнее, чем изменение конфигурации (но либо должно работать). Однако есть проблема: ADO.NET транзакции должны передаваться отдельным командам, поэтому вам понадобится (также немного убирая код):

using(var transaction = conn.BeginTransaction()) {
    try {
        var count = _changeTracker.CommitChanges(conn, transaction);
        transaction.Commit();
        return count;
    } catch {
        transaction.Rollback();
        throw;
    }
}

здесь CommitChanges принимает транзакцию-возможно, используя дополнительные параметры:

int CommitChanges(DbConnection connection, DbTransaction transaction = null)
{ ... }

ваш именование DapperFactory предполагает, что вы используете "dapper" - в этом случае вы можете просто передать это в" dapper", будь то null или нет, т. е.

conn.Execute(sql, args, transaction: transaction);

это во многом зависит от используемой версии SQL Server. См.здесь для другого так вопрос, касающийся аналогичной проблемы.

Это связано с тем, как SQL 2005 и SQL 2008 отличаются в обработке нескольких соединений в пределах одного и того же TransactionScope. т. е. SQL 2008 может открывать несколько соединений в одном TransactionScope без эскалации к MSDTC.

может быть, это проблема, которую вы видите

Если это так, я думаю, только два варианта обновить до SQL 2008 или включить MSDTC. Я понимаю, что оба этих варианта, вероятно, являются огромными головными болями.