问题描述
我具有以下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异步,然后转换类的字段_conn
从DbAdapter
到ThreadLocal<SqlConnection>
是不安全的。原因是在等待异步请求之后,异步工作流很可能将在另一个ThreadPool
线程上继续。因此,错误的连接将被关闭,并且其他一些无关的并发数据访问操作可能会被中断和中止。