TransactionScope 内的 Membership.GetUser() 抛出 TransactionPromotionException

发布于 2024-08-25 23:55:14 字数 1670 浏览 4 评论 0原文

以下代码抛出一个带有消息“事务已中止”的 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" />

阅读的所有内容都告诉我< code>TransactionScope 应该会神奇地应用于会员调用。用户存在(否则我希望返回 null。)

The following code throws a TransactionAbortedException with message "The transaction has aborted" and an inner TransactionPromotionException with message "Failure while attempting to promote transaction":

    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 ...]
        }
    }

The error occurs at the call to Membership.GetUser().

My Connection String is:

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

Everything I've read tells me that TransactionScope should just get magically applied to the Membership calls. The user exists (I'd expect a null return otherwise.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

偏闹i 2024-09-01 23:55:14

TransactionScope 类屏蔽异常。最有可能发生的情况是该范围内的某些内容失败(引发异常),而 TransactionAbortedException 只是控制退出 using 块时发生的副作用。

尝试将 TransactionScope 内的所有内容包装在 try-catch 块中,并在 catch 内重新抛出,并在那里设置断点;你应该能够看到真正的错误是什么。

另一件事是,TransactionScope.Complete 应该是包含 TransactionScopeusing 块结束之前执行的最后一条语句。在这种情况下,您可能应该没问题,因为您之后实际上并没有做任何工作,但将对 Complete 的调用放在内部作用域内往往会产生更容易出现错误的代码。


更新:

现在我们知道了内部异常是什么(促进交易失败),更清楚发生了什么。

问题在于,在 TransactionScope 内,您实际上是使用 GetUser 打开另一个数据库连接。会员资格提供者不知道如何重新使用您已经打开的DataContext;它必须打开自己的连接,当 TransactionScope 看到这一点时,它会尝试升级为分布式事务。

它失败是因为您可能在 Web 服务器、数据库服务器或两者上禁用了 MSDTC。

如果您要打开两个单独的连接,则无法避免分布式事务,因此实际上有几种方法可以解决此问题:

  1. GetUser 调用移至外部 em> TransactionScope。也就是说,首先从成员资格提供程序将用户“读取”到列表中,然后在实际需要开始进行修改时启动事务。

  2. 完全删除 GetUser 调用,并在同一 DataContext 或至少同一连接上直接从数据库读取用户信息。

  3. 在参与事务的所有服务器上启用 DTC(事务升级时性能会受到影响)。

我认为选项#1 在这种情况下是最好的;您需要从会员提供商处读取的数据在您读取数据的时间和开始交易的时间之间不太可能发生更改。

The TransactionScope class masks exceptions. Most likely what's happening is that something inside that scope is failing (throwing an exception), and the TransactionAbortedException is simply a side-effect that occurs when control exits the using block.

Try wrapping everything inside the TransactionScope in a try-catch block, with a rethrow inside the catch, and set a breakpoint there; you should be able to see what the real error is.

One other thing, TransactionScope.Complete should be the last statement executed before the end of the using block containing the TransactionScope. In this case you should probably be alright, since you're not actually doing any work afterward, but putting the call to Complete inside an inner scope tends to make for more bug-prone code.


Update:

Now that we know what the inner exception is (failure promoting transaction), it's more clear what's going on.

The problem is that inside the TransactionScope, you are actually opening up another database connection with GetUser. The membership provider doesn't know how to re-use the DataContext you already have open; it has to open its own connection, and when the TransactionScope sees this, it tries to promote to a distributed transaction.

It's failing because you probably have MSDTC disabled on either the web server, the database server, or both.

There's no way to avoid the distributed transaction if you are going to be opening two separate connections, so there are really a few ways around this issue:

  1. Move the GetUser calls outside the TransactionScope. That is, "read" the users first from the membership provider into a list, then start the transaction when you actually need to start making modifications.

  2. Remove the GetUser calls altogether and read the user information directly from the database, on the same DataContext or at least the same connection.

  3. Enable DTC on all servers participating in the transaction (performance will be impacted when a transaction promotes).

I think that option #1 is going to be the best in this scenario; it's very unlikely that the data you need to read from the membership provider will be changed between the time you read it and the time you begin the transaction.

岁吢 2024-09-01 23:55:14

一方面,这是正确的;另一方面,它也是正确的。事务总是被中止(您没有调用 Complete())。这是确切的代码吗?

此外,将 DataContext 放在 TransactionScope 之外让我怀疑它可能会做一些奇怪的事情,因为当数据存在时事务不存在。 -首先创建上下文。您是否尝试过(两者):

  • 反转创建顺序,以便 TransactionScope 跨越
  • 调用 Complete

DataContext

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

At one level, it is correct; the transaction is always aborted (you aren't calling Complete()). Is that the exact code?

Additionally, having the DataContext outside the TransactionScope makes me suspect that it might be doing some odd things since the transaction isn't there when the data-context first gets created. Have you tried (both of):

  • reversing the creation order, so the TransactionScope spans the DataContext
  • calling Complete

?

using ( TransactionScope transactionScope = new TransactionScope() )
using ( MyDataContext context = new MyDataContext() )
{
    /* ... */
    transactionScope.Complete();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文