Реализация транзакций Linq-to-Sql через WCF - часть II - PullRequest
1 голос
/ 01 мая 2009

Я начал здесь: Реализация транзакций LINQ-to-SQL через WCF

С тех пор я пришел к следующему. Я использую basicHttpBinding для устаревшего, и мой клиент WCF неуправляемый C ++ (gSOAP). Поэтому я использую ASP.NET Session и включаю aspNetCompatibilityMode в WCF. Это работает, так что теперь я могу правильно обрабатывать сессии.

Теперь моя идея заключалась в том, чтобы выделить отдельный LINQ-to-SQL DataContext для каждого сеанса и таким образом добиться транзакционного поведения. Я хотел сохранить экземпляр DataContext внутри Session, выполнить все вставки без вызова SubmitChanges (), а затем просто вызвать SubmitChanges () один раз, чтобы зафиксировать транзакцию.

К сожалению, в LINQ-to-SQL есть ошибка, которая не позволяет вам извлекать ранее вставленные данные из DataContext, прежде чем вызывать SubmitChanges () для него (см. здесь ), и мне нужно это сделать Потому что, прежде чем вставлять тендерные документы и заголовки, мне нужно получить сам тендер. Так что это не сработает (по крайней мере, без полного рефакторинга кода и потери большей части красоты LINQ-to-SQL ...)

Итак, теперь возникает вопрос: как правильно реализовать транзакцию per-DataContext через WCF? Я не могу распространять TransactionScope от клиента, потому что клиент неуправляем. Я не могу использовать внутреннюю транзакцию LINQ-to-SQL из-за ошибки выше. Единственное, что у меня есть, это ASP.NET Session (который работает). Возможно, я могу как-то хранить TransactionScope в Session?

Ответы [ 2 ]

1 голос
/ 13 мая 2009

Я автоматически волнуюсь, как только люди начинают упоминать «транзакция» и «сессия» в одном предложении. По моему мнению и опыту, он просто не подходит для учета нескольких вызовов WCF для одной транзакционной единицы работы:

  • это означает, что ваша блокировка зависит от внешнего клиента
  • большой потенциал для неразрешимых взаимоблокировок - то есть, которые могут быть только тайм-аутом, а не db-центрированными взаимоблокировками, которые могут быть обнаружены
  • Вы вынуждены использовать липкую балансировку нагрузки

Лично я бы попытался работать над одной единицей работы. Это может означать, что клиент упаковывает запрос complete и отправляет его как элементарную операцию, или это может означать, что вы делаете это отдельно через сеанс - т.е. вы буферизуете «что» в нескольких вызовах, но только начинайте что-то делать, когда у вас есть все, что вам нужно.

В любом случае, в итоге вы получите единицу работы, работающую в одной реализации, что должно обеспечить нормальную работу либо транзакций db, либо окружающих (TransactionScope) транзакций. Преимущество окружающей транзакции (в вашем сценарии) состоит в том, что она будет работать в нескольких контекстах данных, но я ожидаю, что должна быть возможность исправить вызов Single (согласно соответствующему вопросу) без слишком большой головной боли.

Или дождитесь .NET 4.0, когда я буду уверен, что ошибка устранена. Хорошо, это последнее, вероятно, бесполезно в краткосрочной перспективе ...

0 голосов
/ 10 мая 2009

Мне кажется, важно то, что вы повторно используете одно и то же соединение и транзакцию. Я бы использовал конструктор на DataContext, который принимает существующее соединение. После запуска сеанса начните транзакцию, выделив соединение и транзакцию. Сохраните соединение и транзакцию в сеансе. Затем при каждом вызове воссоздайте DataContext, используя одно и то же соединение и транзакцию. Когда закончите, вызовите метод, который фиксирует транзакцию. По истечении времени ожидания сеанса вам нужно будет откатить транзакцию.

 public bool CreateTransaction()
 {
     var connection = new SqlConnection( connectionString );
     var transaction = connection.BeginTransaction();
     Session["DBConnection"] = connection;
     Session["DBTransaction"] = transaction;

     return true;
 }

 private DBDataContext CreateContextFromSession()
 {
      if (Session["DBConnection"] == null)
          throw new NullReferenceException( "No connection available for transaction." );
      if (Session["DBTransaction"] == null)
          throw new NullReferenceException( "No transaction available." );

      var context = new DBDataContext( (IDbConnection)Session["DBConnection"] );
      context.Transaction = (DbTransaction)Session["DBTransaction"];

      return context;
 }

 public Tender CreateTender( ... )
 {
     var context = CreateContextFromSession();

     var tender = new Tender { ... };
     context.Tenders.InsertOnSubmit( tender );
     context.SubmitChanges();

     return tender;
 }

 public void CommitTransaction()
 {
     var transaction = (DbTransaction)Session["DBTransaction"];
     transaction.Commit();
     Session.Remove( "DBTransaction" );

     var connection = (IDbConnection)Session["DBConnection"];
     connection.Close();
     Session.Remove( "DBConnection" );
 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...