Вложенные TransactionScope и / или вложенные соединения, вызывающие эскалацию MSDTC
Я пытаюсь избежать эскалации MSDTC в моем приложении. Я использую LINQ с SQL Server Express 2008 R2, а позже буду использовать полную версию.
Я написал класс-оболочку базы данных, который создает необходимые соединения и избавляется от них как можно быстрее. Строка подключения остается одинаковой для всех подключений.
Вот очень урезанная версия моего класса:
public class SqlServerDatabaseWrapper {
public SqlServerDatabaseWrapper(string connectionString) {
ConnectionString = connectionString;
}
public string ConnectionString { get; private set; }
private static IDbConnection GetOpenConnection() {
var conn = new SqlConnection(ConnectionString);
conn.Open();
return conn;
}
// there is also a second method to return a value
// there is PerformCommandAction for SqlCommand as well
public void PerformDataContextAction<TContext>(Func<IDbConnection, TContext> creator, Action<TContext> action) where TContext : DataContext {
PerformConnectionAction(conn => {
using (var context = creator(conn))
action(context);
});
}
// there is also a second method to return a value
public void PerformConnectionAction(Action<IDbConnection> action) {
using (IDbConnection conn = GetOpenConnection(ConnectionString)) {
action(conn);
}
}
}
Используется следующим образом:
var db = new SqlServerDatabaseWrapper(connectionString);
db.PerformDataContextAction(
conn => new SomeDataContext(conn),
context => { /* do something */ }
);
Если я наложу блокировку на содержимое метода PerformConnectionAction, чтобы за один раз мог работать только один, тогда все работает, но есть заметное снижение производительности. Однако, когда я удаляю его, он обостряется.
Код, который использует оболочку, использует TransactionScope, и может быть вложение TransactionScopes и / или вызовов либо PerformDataContextAction, либо PerformConnectionAction (каждый из которых создает новое соединение с одной и той же строкой соединения); в псевдокоде (как это может происходить в разных классах / методах):
var db = new SqlServerDatabaseWrapper(connectionString)
using (TransactionScope tran = new TransactionScope()) {
db.PerformDataContextAction(
/* ... */,
context => {
using (TransactionScope tran2 = new TransactionScope()) {
db.PerformConnectionAction(conn => { /* some stuff */ });
tran2.Complete();
}
}
tran.Complete();
}
Обратите внимание также, что есть использование статических методов Членства, которые могут происходить в различных точках.
Я также должен добавить, что строка подключения выглядит следующим образом:
Data Source=.\SQLEXPRESS;Initial Catalog=db1;User Id=test1;Password=test1;MultipleActiveResultSets=true;Enlist=false;
Вопрос в том, как я могу реорганизовать / переписать мой код, чтобы мое приложение могло работать хорошо, без MSDTC и без введения блокировок?
Спасибо
1 ответ
Используете ли вы только одно соединение с базой данных в рамках транзакции? Создание двух подключений с одинаковой или другой строкой подключения в области транзакции приведет к расширению транзакции до распределенной.
Вы можете сохранить соединение в статической переменной потока и закрыть / удалить его, когда вся работа в транзакции будет выполнена. Тогда каждый поток будет иметь свое собственное соединение.
Когда вы добавляете блокировку к вашей логике, вы, вероятно, не получите распределенную транзакцию, потому что пул соединений будет возвращать вам одно и то же соединение каждый раз.