Membership.GetUser () в TransactionScope генерирует исключение TransactionPromotionException - PullRequest
3 голосов
/ 21 марта 2010

Следующий код выдает TransactionAbortedException с сообщением «Транзакция прервана» и внутреннее TransactionPromotionException с сообщением «Сбой при попытке повысить транзакцию»:

    using ( TransactionScope transactionScope = new TransactionScope() )
    {
        try
        {
            using ( MyDataContext context = new MyDataContext() )
            {
                Guid accountID = new Guid( Request.QueryString[ "aid" ] );
                Account account = ( from a in context.Accounts where a.UniqueID.Equals( accountID ) select a ).SingleOrDefault();
                IQueryable < My_Data_Access_Layer.Login > loginList = from l in context.Logins where l.AccountID == account.AccountID select l;

                foreach ( My_Data_Access_Layer.Login login in loginList )
                {
                    MembershipUser membershipUser = Membership.GetUser( login.UniqueID );
                }

                [... lots of DeleteAllOnSubmit() calls]

                context.SubmitChanges();
                transactionScope.Complete();
            }   
        }

        catch ( Exception E )
        {
        [... reports the exception ...]
        }
    }

Ошибка возникает при звонке на Membership.GetUser().

Моя строка подключения:

      <add name="MyConnectionString" connectionString="Data Source=localhost\SQLEXPRESS;Initial Catalog=MyDatabase;Integrated Security=True"
   providerName="System.Data.SqlClient" />

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

Ответы [ 2 ]

6 голосов
/ 21 марта 2010

Класс TransactionScope маскирует исключения.Скорее всего, происходит то, что что-то внутри этой области выходит из строя (вызывает исключение), а TransactionAbortedException - это просто побочный эффект, возникающий при выходе из блока using.

Попробуйте обернуть все внутриTransactionScope в блоке try-catch, с перебросом внутри catch и установкой там точки останова;вы должны увидеть реальную ошибку.

Еще одна вещь, TransactionScope.Complete должна быть последним оператором, выполненным до конца блока using, содержащего TransactionScope.В этом случае вы, вероятно, должны быть в порядке, так как на самом деле вы не выполняете какую-либо работу впоследствии, но помещаете вызов Complete во внутреннюю область видимости, что приводит к более подверженному ошибкам коду.


Обновление:

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

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

Это происходит сбой, потому что вы, вероятно, отключили MSDTC на любом веб-сервере, сервере базы данных,или и то, и другое.

Невозможно избежать распределенной транзакции, если вы собираетесь открыть два отдельных соединения, поэтому существует несколько способов решения этой проблемы:

  1. Переместите GetUser звонки за пределы TransactionScope.То есть сначала «прочитайте» пользователей из поставщика членства в список, а затем начните транзакцию, когда вам действительно нужно начать вносить изменения.

  2. Удалите вызовы GetUser в целом ичитать информацию о пользователях непосредственно из базы данных на том же DataContext или, по крайней мере, на том же соединении.

  3. Включить DTC на всех серверах, участвующих в транзакции (на производительность будет влиять, когдатранзакция продвигается).

Я думаю, что вариант № 1 будет лучшим в этом сценарии;Маловероятно, что данные, которые вам нужно прочитать от поставщика членства, будут меняться между временем, когда вы их читаете, и временем, когда вы начинаете транзакцию.

1 голос
/ 21 марта 2010

На одном уровне это правильно; транзакция всегда прерывается (вы не вызываете Complete ()). Это точный код?

Кроме того, наличие DataContext за пределами TransactionScope заставляет меня подозревать, что он может делать какие-то странные вещи, поскольку транзакция не выполняется, когда впервые создается контекст данных. Вы пробовали (оба):

  • обратный порядок создания, поэтому TransactionScope охватывает DataContext
  • звонит Complete

using ( TransactionScope transactionScope = new TransactionScope() )
using ( MyDataContext context = new MyDataContext() )
{
    /* ... */
    transactionScope.Complete();
}
...