Вложенные TransactionScope и / или вложенные соединения, вызывающие эскалацию MSDTC - PullRequest
5 голосов
/ 13 марта 2012

Я пытаюсь избежать эскалации 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();
}

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

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

Data Source=.\SQLEXPRESS;Initial Catalog=db1;User Id=test1;Password=test1;MultipleActiveResultSets=true;Enlist=false;

Вопрос в том, как мне изменить или переписать мой код, чтобы мое приложение могло работать нормально, без MSDTC и без введения блокировок?

Спасибо

1 Ответ

1 голос
/ 02 мая 2012

Используете ли вы только одно соединение с базой данных в рамках транзакции? Создание двух подключений с одинаковой или другой строкой подключения внутри области транзакции приведет к расширению транзакции до распределенной.

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

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

...