c# – 使用交易镜像时停止交易分配的推荐做法

前端之家收集整理的这篇文章主要介绍了c# – 使用交易镜像时停止交易分配的推荐做法前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
使用TransactionScope对象设置不需要跨函数调用传递的隐式事务是非常好的!但是,如果连接已打开,而另一个已经打开,则事务协调器将静默地升级要分发的事务(需要MSDTC服务才能运行并占用更多的资源和时间).

所以,这很好:

using (var ts = new TransactionScope())
        {
            using (var c = DatabaseManager.GetOpenConnection())
            {
                // Do Work
            }
            using (var c = DatabaseManager.GetOpenConnection())
            {
                // Do more work in same transaction using different connection
            }
            ts.Complete();
        }

但这会升级​​交易:

using (var ts = new TransactionScope())
        {
            using (var c = DatabaseManager.GetOpenConnection())
            {
                // Do Work
                using (var nestedConnection = DatabaseManager.GetOpenConnection())
                {
                    // Do more work in same transaction using different nested connection - escalated transaction to distributed
                }
            }
            ts.Complete();
        }

有没有推荐的做法,以避免以这种方式升级的交易,同时仍然使用嵌套连接?

目前我可以想出的最好的方法是使用ThreadStatic连接,并重用如果Transaction.Current被设置,就像这样:

public static class DatabaseManager
{
    private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true";

    [ThreadStatic]
    private static sqlConnection _transactionConnection;

    [ThreadStatic] private static int _connectionNesting;

    private static sqlConnection GetTransactionConnection()
    {
        if (_transactionConnection == null)
        {
            Transaction.Current.TransactionCompleted += ((s,e) =>
            {
                _connectionNesting = 0;
                if (_transactionConnection != null)
                {
                    _transactionConnection.Dispose();
                    _transactionConnection = null;
                }
            });

            _transactionConnection = new sqlConnection(_connectionString);
            _transactionConnection.Disposed += ((s,e) =>
            {
                if (Transaction.Current != null)
                {
                    _connectionNesting--;
                    if (_connectionNesting > 0)
                    {
                        // Since connection is nested and same as parent,need to keep it open as parent is not expecting it to be closed!
                        _transactionConnection.ConnectionString = _connectionString;
                        _transactionConnection.Open();
                    }
                    else
                    {
                        // Can forget transaction connection and spin up a new one next time one's asked for inside this transaction
                        _transactionConnection = null;
                    }
                }
            });
        }
        return _transactionConnection;
    }

    public static sqlConnection GetOpenConnection()
    {
        sqlConnection connection;
        if (Transaction.Current != null)
        {
            connection = GetTransactionConnection();
            _connectionNesting++;
        }
        else
        {
            connection = new sqlConnection(_connectionString);
        }
        if (connection.State != ConnectionState.Open)
        {
            connection.Open();
        }
        return connection;
    }
}

编辑:所以,如果答案是重用同一个连接,当它嵌套在一个事务镜像中时,像上面的代码一样,我想知道处理这个连接中间事务的含义.

只要看到(使用Reflector检查代码),连接的设置(连接字符串等)将被重置,并且连接被关闭.所以(理论上),重新设置连接字符串并在后续调用中打开连接,应该“重用”连接并防止升级(我的初始测试与此同步).

它确实看起来有点黑客,但是我确信必须有一个最佳实践的地方说,一个人不应该继续使用一个对象后,被处置!

然而,由于我无法对密封的sqlConnection进行子类化,并且希望保持与事务无关的连接池友好方法,所以我努力(但会很高兴)看到更好的方法.

此外,意识到如果应用程序代码尝试打开嵌套连接(在大多数情况下是不必要的,在我们的代码库中),我可以通过抛出异常来强制非嵌套连接

public static class DatabaseManager
{
    private const string _connectionString = "data source=.\\sql2008; initial catalog=test; integrated security=true; enlist=true;Application Name='jimmy'";

    [ThreadStatic]
    private static bool _transactionHooked;
    [ThreadStatic]
    private static bool _openConnection;

    public static sqlConnection GetOpenConnection()
    {
        var connection = new sqlConnection(_connectionString);
        if (Transaction.Current != null)
        {
            if (_openConnection)
            {
                throw new ApplicationException("Nested connections in transaction not allowed");
            }

            _openConnection = true;
            connection.Disposed += ((s,e) => _openConnection = false);

            if (!_transactionHooked)
            {
                Transaction.Current.TransactionCompleted += ((s,e) =>
                {
                    _openConnection = false;
                    _transactionHooked = false;
                });
                _transactionHooked = true;
            }
        }
        connection.Open();
        return connection;
    }
}

仍然值得一个较少的黑客解决方案:)

解决方法

事务升级的主要原因之一是在事务中涉及多个(不同的)连接.这几乎总是升级到分布式事务.这确实是一种痛苦.

这就是为什么我们确保我们所有的事务都使用一个连接对象.有几种方法可以做到这一点.在大多数情况下,我们使用线程静态对象来存储一个连接对象,而我们的数据库持久化的类可以使用线程静态连接对象(当然这是共享的).这样可以防止多个连接对象被使用,并且消除了事务升级.您也可以通过简单地将连接对象从方法传递给方法来实现,但是这并不像IMO那样干净.

原文链接:https://www.f2er.com/csharp/93286.html

猜你在找的C#相关文章