在DAL类ThreadLocal中建立SqlConnection是否安全?

问题描述

我具有以下DAL类层次结构(部分显示),用于抽象对数据库的数据访问。我想以线程安全的方式使用它:

public class DbAdapter : IDbAdapter,Idisposable
{
    private sqlConnection _conn;
    private readonly string _connString;

    protected DbAdapter(string connString)
    {
        if (string.IsNullOrWhiteSpace(connString))
            throw new ArgumentException("Value cannot be null,empty or whitespace",nameof(connString));
        _connString = connString;
    }

    public void dispose()
    {
        CloseConnection();
    }

    public sqlConnection GetConnection()
    {

        if (_conn == null || _conn.State == ConnectionState.Closed)
            _conn = new sqlConnection(_connString);
        else if (_conn.State == ConnectionState.broken)
        {
            _conn.Close();
            _conn.open();
        }
        return _conn;
    }

    public void CloseConnection()
    {
        if (_conn != null && _conn.State == ConnectionState.Open)
            _conn.Close();
        _conn = null;
    }

    public sqlCommand GetCommand(string query,sqlTransaction transaction)
    {
        var cmd = new sqlCommand
        {
            Connection = transaction != null ? transaction.Connection : GetConnection()
        };
        if (transaction != null)
            cmd.Transaction = transaction;
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = query;
        cmd.CommandTimeout = 500;
        return cmd;
    }
    
    // Omitted other methods
}

public class PersonDba : DbAdapter 
{
    //Omitted 

    public IList<Person> GetPersons(string whereClause,string orderClause,sqlTransaction transaction = null)
    {
        var query = "SELECT Id,Name,PersonId,Birthdate,Modified FROM Persons ";

        if (!string.IsNullOrWhiteSpace(whereClause))
            query += whereClause;
        if (!string.IsNullOrWhiteSpace(orderClause))
            query += orderClause;

        IList<Person> result = new List<Person>();

        var sqlCmd = GetCommand(query,transaction);
        using (var reader = sqlCmd.ExecuteReader(CommandBehavior.CloseConnection))
        {
            while (reader.Read())
            {
                var person = new Person
                {
                    Id = reader.GetInt32(reader.Getordinal("Id")),PersonId = reader.GetInt32(reader.Getordinal("PersonId")),Name = reader.GetString(reader.Getordinal("Name")),Birthdate = reader.GetDateTime(reader.Getordinal("Birthdate")),LastModified = reader.GetDateTime(reader.Getordinal("Modified"))
                };
                result.Add(person);
            }
        }
        return result;
    }
}

通常,我将PersonDba的单个实例注入其他包含多线程代码的类中。为了避免在相关代码中锁定对单个实例的所有访问,我正在考虑将sqlConnection DbAdapter._conn设置为ThreadLocal<sqlConnection>类型(请参阅ThreadLocal)。足以确保使用此类的实例是线程安全的吗?

解决方法

假设您想保持开放的可能性,将来可以通过使用ExecuteReaderAsync而不是ExecuteReader来使DAL异步,然后转换类的字段_connDbAdapterThreadLocal<SqlConnection>是不安全的。原因是在等待异步请求之后,异步工作流很可能将在另一个ThreadPool线程上继续。因此,错误的连接将被关闭,并且其他一些无关的并发数据访问操作可能会被中断和中止。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...