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. Я понимаю, что оба этих варианта, вероятно, являются огромными головными болями.