ADO.NET を使用して既存のコードを並列処理シナリオに変換する必要があります。元のコードは静的接続オブジェクトとトランザクションを使用しているため、かなりの再構築が必要でした。
概念実証として、Task Parallel Library を使用して、いくつかの単純な挿入を安定した方法で動作させることに挑戦してきました。そのためには、奇妙なコードを書かなければならないことがわかりました。まず、コンソール アプリの 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();
}
}
これを機能させるために必要な 2 つの奇妙なビットは次のとおりです。
- 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>
接続プールをオフにする必要があることがわかりました。
誰かが接続とさまざまなスレッド (タスク) を使用する最良の方法を説明できる場合は、私は大いに感謝しています。