我必须将一些使用 ADO.NET 的现有代码转换为并行处理方案。原始代码使用静态连接对象和事务,因此需要进行一些相当大的重组。
作为概念证明,我一直在尝试使用任务并行库让一些简单的插入以稳定的方式工作。我发现我必须编写一些奇怪的代码才能做到这一点。首先,我的控制台应用程序中的 Main 方法:
class Program
{
private static Db db1;
private static Db db2;
static void Main(string[] args)
{
db1 = new Db();
db2 = new Db();
Task task1 = Task.Factory.StartNew(() =>
{
db1.BeginTransaction("Transaction1");
decimal p = AddNewProject(db1, "Project 1");
AddNewTask(db1, p, "Task1");
});
Task task2 = Task.Factory.StartNew(() =>
{
db2.BeginTransaction("Transaction1");
decimal p = AddNewProject(db2, "Project 2");
AddNewTask(db2, p, "Task1");
});
db1.Dispose();
db2.Dispose();
Console.ReadLine();
}
static decimal AddNewProject(Db db, string name)
{
return db.InsertNewProject(name);
}
static void AddNewTask(Db db, decimal project, string name)
{
db.InsertNewTask(project, name);
}
}
重要的是,带有奇怪代码的 Db 类:
public class Db : IDisposable
{
private SqlConnection connection;
private String connString;
private SqlTransaction transaction;
public Db()
{
connString = ConfigurationManager.ConnectionStrings["MyConn"].ConnectionString;
this.connection = new SqlConnection();
this.connection.StateChange += new StateChangeEventHandler(connection_StateChange);
this.connection.ConnectionString = connString;
this.connection.Open();
}
void connection_StateChange(object sender, StateChangeEventArgs e)
{
if (e.CurrentState != ConnectionState.Open)
{
this.connection.ConnectionString = this.connString;
this.connection.Open();
}
}
public void BeginTransaction(string vendorName)
{
while (this.connection.State != ConnectionState.Open)
{
Thread.Sleep(10);
}
this.transaction = this.connection.BeginTransaction(IsolationLevel.ReadUncommitted, vendorName);
}
public decimal InsertNewProject(string name)
{
SqlCommand command = new SqlCommand();
command.Connection = this.connection;
command.CommandType = CommandType.Text;
if (this.connection.State != ConnectionState.Open)
{
this.connection.ConnectionString = this.connString;
this.connection.Open();
}
command.CommandText = "INSERT INTO Project VALUES('" + name + "', 'true');SELECT SCOPE_IDENTITY();";
command.Transaction = this.transaction;
Object pk = command.ExecuteScalar();
return (decimal)pk;
}
public int InsertNewTask(decimal project, string name)
{
SqlCommand command = new SqlCommand();
command.Connection = this.connection;
command.CommandType = CommandType.Text;
if (this.connection.State != ConnectionState.Open)
{
this.connection.ConnectionString = this.connString;
this.connection.Open();
}
command.CommandText = "INSERT INTO Task([TaskName], [Project], [Visible], [Estimate]) VALUES('" + name + "', '" + (int)project + "', 'true', 0);SELECT SCOPE_IDENTITY();";
command.Transaction = this.transaction;
Object pk = command.ExecuteScalar();
this.transaction.Commit();
//this.transaction.Rollback();
return Convert.ToInt32((decimal)pk);
}
public void Dispose()
{
this.connection.Close();
//this.transaction.Dispose();
this.connection.Dispose();
}
}
完成这项工作所需的两个奇怪的位是:
- StateChange 处理程序(以及为什么我必须处理这个?!)
- BeginTransaction 方法,以及该类的方法中对连接的不断检查和打开
为什么连接不保持打开状态?曾经那么奇怪。这段代码让我很紧张,我并不急于将它放入 UAT(然后是生产)。
连接字符串是:
<connectionStrings>
<add name="MyConn" connectionString="Data Source=PLAGUIS;Initial Catalog=TimeTracker;Pooling=false;MultipleActiveResultSets=True;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
我发现我必须关闭连接池。
如果有人可以解释使用连接和不同线程(任务)的最佳方式,我将非常感激。