Simpleroleprovider导致transactionscope内的远程事务 [英] Simpleroleprovider causing remote transaction inside transactionscope

查看:96
本文介绍了Simpleroleprovider导致transactionscope内的远程事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将asp.net成员资格升级到MVC4中的新的simplemembershipship提供商.这是一个Azure/Sql Azure应用,可在localhost上正常运行,但在部署时会失败.我在事务中有如下代码:

            TransactionOptions toptions = new TransactionOptions();
            toptions.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
            using (TransactionScope trans = new TransactionScope(TransactionScopeOption.Required, toptions))
            {
                try
                {
                   ... do a bunch of database stuff in a single dbContext ...

                   var roleprov = (SimpleRoleProvider)Roles.Provider;
                   string[] roles = roleprov.GetRolesForUser(Username);
                   // above line fails with The transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D024)
                 }
             }

我正在使用此技术填充Roles类.堆栈跟踪似乎表明它确实在尝试触发子事务以完成该调用. simplemembership表位于不同的数据库中.如何在单独事务的上下文中从角色提供者检索角色信息?

解决方案

问题是GetRolesForUser导致新连接打开到第二个数据库,而该连接反过来又发现它在TransactionScope中.反过来( MSDN-与SQL Server的System.Transactions集成),然后晋升为DTC.您可以尝试一些选择:

在交易开始前获取角色

您可以在TransactionScope中检索string[] roles 外部.您是否有理由将它们放入示波器?假设您说:

如何在单独事务的上下文中从角色提供者检索角色信息

听起来您可以在TransactionScope之前获得角色信息,并且没有问题.

关闭简单成员资格连接字符串上的交易

通过输入"enlist = false",可以告诉连接字符串不参与交易(请参阅

对于SimpleRoleProvider,它创建它的数据库对象,然后在第一次使用它时将其打开.但是,直到... 才关闭它. 请注意,每次调用GetRolesForUser时都会打开连接,所以您很不走运.我以为您可以在打开TransactionScope之前先调用GetRolesForUser,然后使用已经打开的连接再次在示波器内部-您不能.

IObjectContextAdapter

免责声明:由于无法测试您的设置,因此我不能保证这会起作用.

您可以通过在事务范围之外打开非事务连接字符串来防止两个连接字符串升级的技巧,然后再不应该升级该事务.如果您导致同一事务范围内的Close然后是Open的相同连接(否则会导致升级),也可以使用此方法.

您可以在您的上下文中尝试此操作,看看是否阻止了GetRolesForUser促进交易,但是我怀疑这样做是否会起作用,因为GetRolesForUser会导致连接打开(如果尚未打开).由于无法在您的情况下进行测试,因此会在可能的情况下提供它.

using (var db = new ExampleContext())
{
    var adapter = db as System.Data.Entity.Infrastructure.IObjectContextAdapter;
    using (var conn = adapter.ObjectContext.Connection)
    {
        conn.Open();
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
        {
            // perform operations
            db.SaveChanges();
            // perform more operations
            db.SaveChanges();
            // perform even more operations
            db.SaveChanges();

            //  If you don't complete, the transaction won't commit and you will lose the changes
            scope.Complete();
        }
    }
}

I am in the process of upgrading asp.net membership to the new simplemembership provider in MVC4. This is an Azure/Sql Azure app which runs fine on localhost but fails when deployed. I have code in a transaction as follows:

            TransactionOptions toptions = new TransactionOptions();
            toptions.IsolationLevel = System.Transactions.IsolationLevel.Serializable;
            using (TransactionScope trans = new TransactionScope(TransactionScopeOption.Required, toptions))
            {
                try
                {
                   ... do a bunch of database stuff in a single dbContext ...

                   var roleprov = (SimpleRoleProvider)Roles.Provider;
                   string[] roles = roleprov.GetRolesForUser(Username);
                   // above line fails with The transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D024)
                 }
             }

I am using this technique to populate the Roles classes. The stack trace seems to indicate that it is indeed trying to fire off a sub-transaction to complete that call. The simplemembership tables are in a different db. How can I retrieve role info from the role provider inside the context of a separate transaction?

解决方案

The problem is that GetRolesForUser causes a new connection to open to a second database, and that in turn picks up that it is in a TransactionScope. In turn this (MSDN - System.Transactions Integration with SQL Server) then promotes to the DTC. You could try a few options:

Get roles before the transaction starts

You could retrieve string[] roles outside your TransactionScope. Is there a reason you need to get them inside the scope? Given that you say:

How can I retrieve role info from the role provider inside the context of a separate transaction

it sounds like you could get the role info before the TransactionScope and have no problems.

Turn off transactions on the simple membership connection string

You can tell a connection string not to take part in transactions by putting "enlist=false" (see SqlConnection.ConnectionString) in the connection string, so this might be one option for you if you never need transactions on the database you use for Simple Membership.

Try opening the Simple Membership connection before the transaction

For SimpleRoleProvider it creates it's database object, and then opens it the first time it uses it. But, it doesn't close it until .... Scratch that, the connection is opened on each call to GetRolesForUser so you are out of luck. I was thinking you could call GetRolesForUser once before TransactionScope is opened, and then again inside the scope using the already open connection - you can't.

Play with the IObjectContextAdapter

Disclaimer: I can't promise this will work as I can't test with your setup.

You can play tricks to prevent promotion with two connection strings by opening the non-transaction connection string outside the transaction scope first, and then the transaction shouldn't be promoted. This can also be used if you cause the same connection to Close and then Open inside the same transaction scope (which would otherwise cause promotion).

You could try this with your context, and see if that stopped the GetRolesForUser promoting the transaction, but I doubt that would work as GetRolesForUser causes the connection to open if it isn't already. As I can't test in your scenario, I will include it in case it helps.

using (var db = new ExampleContext())
{
    var adapter = db as System.Data.Entity.Infrastructure.IObjectContextAdapter;
    using (var conn = adapter.ObjectContext.Connection)
    {
        conn.Open();
        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
        {
            // perform operations
            db.SaveChanges();
            // perform more operations
            db.SaveChanges();
            // perform even more operations
            db.SaveChanges();

            //  If you don't complete, the transaction won't commit and you will lose the changes
            scope.Complete();
        }
    }
}

这篇关于Simpleroleprovider导致transactionscope内的远程事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆