【问题标题】:Simpleroleprovider causing remote transaction inside transactionscopeSimpleroleprovider 导致事务范围内的远程事务
【发布时间】:2013-05-22 00:08:41
【问题描述】:

我正在将 asp.net 成员资格升级到 MVC4 中的新 simplemembership 提供程序。这是一个 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)
                 }
             }

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

【问题讨论】:

    标签: asp.net-mvc-4 transactionscope simplemembership distributed-transactions


    【解决方案1】:

    问题是GetRolesForUser 导致一个新连接打开到第二个数据库,而这反过来又发现它在TransactionScope 中。反过来,这个 (MSDN - System.Transactions Integration with SQL Server) 然后提升到 DTC。您可以尝试几个选项:

    在事务开始前获取角色

    您可以在您的TransactionScope 外部检索string[] roles。您是否有理由需要将它们纳入范围?鉴于你说:

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

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

    关闭简单会员连接字符串上的事务

    您可以通过在连接字符串中添加“enlist=false”(请参阅​​SqlConnection.ConnectionString)来告诉连接字符串不参与事务,因此如果您从不需要数据库上的事务,这可能是您的一种选择用于简单会员。

    尝试在交易前打开简单会员连接

    对于SimpleRoleProvider,它会创建它的数据库对象,然后在第一次使用它时打开它。但是,直到 ... 才会关闭它。 从头开始,每次调用 GetRolesForUser 时都会打开连接,所以你运气不好。我在想你可以在打开TransactionScope 之前调用GetRolesForUser 一次,然后在范围内使用已经打开的连接再次调用 - 你不能。

    IObjectContextAdapter

    免责声明:我不能保证这会起作用,因为我无法使用您的设置进行测试。

    您可以通过首先打开事务范围之外的非事务连接字符串来防止使用两个连接字符串进行提升,然后不应该提升该事务。如果您在同一事务范围内导致与CloseOpen 的相同连接(否则会导致升级),也可以使用此方法。

    您可以在您的上下文中尝试此操作,看看这是否会阻止 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();
            }
        }
    }
    

    【讨论】:

    • 最后,我找到了一种将角色拉出事务范围的方法。这有点尴尬,但它奏效了。我找到了一篇关于这个问题的好文章here
    • @AldenG。是的,所以 ("当你有多个连接到不同的数据库时。) 证实了我上面所说的。最后你使用了SqlTransaction 吗?我假设你需要这个选项,所以我把这个选项排除在外坚持使用 Roles 和 DbContext 以减少与数据库的耦合。
    • 我用 SqlTransaction 玩了一个小时,但找不到让它与实体框架很好地配合的方法。将其从事务范围中拉出来是最简单的解决方案,并且通过几个扩展方法来访问角色,代码最终变得足够干净。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 2011-05-28
    相关资源
    最近更新 更多