TransactionScope中的Membership.GetUser()抛出TransactionPromotionException
以下代码抛出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 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()
时发生错误。
我的连接字符串是:
我读过的所有内容都告诉我, TransactionScope
应该神奇地应用于会员电话。 用户存在(否则我希望返回null。)
TransactionScope
类掩盖了exception。 最有可能发生的事情是该范围内的某些内容失败(抛出exception),而TransactionAbortedException
只是当控件退出using
块时发生的副作用。
尝试将TransactionScope
中的所有内容包装在try-catch
块中,并在catch
重新抛出,并在那里设置断点; 你应该能够看到真正的错误是什么。
另一件事, TransactionScope.Complete
应该是在包含TransactionScope
的using
块结束之前执行的最后一个语句。 在这种情况下,你可能应该没问题,因为之后你实际上并没有做任何工作,但是在内部范围内调用Complete
会导致更多容易出错的代码。
更新:
既然我们知道内部exception是什么(促进事务失败),那么更清楚的是发生了什么。
问题是在TransactionScope
,您实际上是在使用GetUser
打开另一个数据库连接。 成员资格提供者不知道如何重新使用您已经打开的DataContext
; 它必须打开自己的连接,当TransactionScope
看到它时,它会尝试升级到分布式事务。
它失败了,因为您可能在Web服务器,数据库服务器或两者上都禁用了MSDTC。
如果要打开两个单独的连接,则无法避免分布式事务,因此有很多方法可以解决此问题:
-
将
TransactionScope
调用GetUser
TransactionScope
外部 。 也就是说,首先从会员提供者“读取”用户到列表中,然后在实际需要开始修改时启动事务。 -
完全删除
GetUser
调用并直接从数据库,同一DataContext
或至少相同的连接读取用户信息。 -
在参与事务的所有服务器上启用DTC(当事务提升时,性能将受到影响)。
我认为选项#1在这种情况下将是最好的; 您需要从会员提供商处读取的数据在您阅读它和开始交易的时间之间进行更改是不太可能的。
在一个层面上,它是正确的; 事务总是中止(你没有调用Complete())。 这是确切的代码吗?
此外,在TransactionScope
外部使用DataContext
会让我怀疑它可能正在做一些奇怪的事情,因为在创建数据上下文时事务不存在。 你试过(两个):
- 反转创建顺序,因此
TransactionScope
跨越DataContext
- 呼叫
Complete
?
using ( TransactionScope transactionScope = new TransactionScope() ) using ( MyDataContext context = new MyDataContext() ) { /* ... */ transactionScope.Complete(); }