Azure,SQL事务和连接

时间:2017-12-11 10:16:34

标签: c# sql-server linq azure

问题: 如何将Linq,Azure和数据库连接与transactionscope相结合,而不会将事务提升为分布式?

具体来说,变通方法/解决方案2(见下文)是一个可接受的解决方案,还是有更好的方法?

背景资料:

从.config文件中获取连接字符串。

using (var db = new DBDataContext())
using (var scope = new TransactionScope())
{
    // Do DB stuff, then call a method that has more Linq code..
    //..accessing the _same_ DB, and needs to be inside this transaction.
}

这似乎是最佳做法,在localhost上调试时工作正常。

当部署到Azure时,只要我们使用完全相同的连接字符串,就会在执行Linq查询时将事务“提升”为分布式。这会导致运行时异常。

请注意,被调用的方法有自己的“using DBDataContext()”,但没有新的transactioncope。

似乎连接池管理器不确定新连接是否属于同一个数据库,即使连接字符串相同。

似乎有3种解决方法:

1)传递对现有连接的引用    - 不能接受的。实际上有数百种调用DB的方法。这不是来电者的责任。

2)使用全局(数据层)连接管理器     - 这不是最佳做法,应该避免。但为什么呢?

3)使用集成安全性     - 使用集成安全性时,连接池管理器可以将连接识别为与现有连接相同。     - 没有测试过,因为这个解决方案是不可接受的。由于这个问题,不应该被迫使用集成安全性。

编辑:

使用Azure SQL数据库(不是Azure VM上的SQL Server)。

Azure SQL数据库不支持分布式事务。

1 个答案:

答案 0 :(得分:1)

你在这里回答了自己的问题:

  

当部署到Azure时,交易被提升并且#39;尽管我们使用的是完全相同的连接字符串,但只要执行了Linq查询,就可以分发。这会导致运行时异常。

每次涉及第二个连接时,交易都会提升。可能有一些优化案例可以绕过这一点(我不知道任何事情),但我不认为你可以在这里做很多事情。应该重用相同的连接。

想想没有TransactionScope它会如何工作。您的代码可能如下所示:

using (var cn = GetDbConnection())
using (var tx = cn.BeginTransaction()) 
{
    // do stuff with tx...

    using (var cn2 = GetDbConnection())
    {
        // Now think about the transaction scope here... 
        // There is no way for cn2 to reuse cn's transaction.
        // It must begin its own transaction. The only way to manage 
        // disconnected transactions of this nature is to elevate to
        //  a distributed transaction.
    }
}

编辑:关于您关于全球连接管理器的问题,我不确定这是一个坏主意,具体取决于您的实施。对于ASP.NET用例,我们通常根据请求确定数据库上下文的范围。链中需要连接的任何代码都应该注入其数据库上下文。

这可确保在整个请求中共享相同的上下文(连接)。然后可以自动或手动提交事务,或者在异常情况下自动回滚事务。这是一个非常简单的用例,并且可能不适合您的方案,但它对我们来说效果很好。

Edit2:使用轻量级事务,可以在打开下一个连接之前关闭一个连接来避免提升。交易本身保持打开状态,直到您拨打ts.Complete,即使是跨越连接。

https://blogs.msdn.microsoft.com/adonet/2008/03/25/extending-lightweight-transactions-in-sqlclient/

  

您打开外部连接“A”。该池没有可用的适当连接,因此在事务中建立并登记内部连接“z”,从而建立轻量级事务。你现在关闭“A”,它会留出“z”来等待交易结束。接下来打开外部连接“B”(您也可以再次打开“A”并获得相同的结果)。 “B”在附加到事务的池中查找自由内部连接,找不到,创建内部连接“y”并尝试在事务中登记它。现在发现要尝试登记的两个不同资源的事务必须提升(一般资源,特别是sql连接,不能共享本地事务)。最后,您结束事务,该事务通过“z”发送提交或回滚,将其与事务断开连接并将其返回到池中。

     

因此,这将我们带到我们为Sql Server 2008支持添加的扩展。在服务器上,我们添加了一个不会回滚本地事务的新连接重置模式。这允许SqlClient将内部连接返回到池以供重用。在我们的示例中,当您打开“B”时,它将在池中找到“z”,与“A”在您关闭“A”时放置的事务相关联。 “B”挪用并重置“z”(保持交易保留重置)并愉快地继续工作。 System.Transaction和服务器都不知道应用程序将“z”视为两个单独的连接。就他们而言,只有一个连接,只处理一个本地交易,不需要升级。