SQL Serverに関してのみのやり方ですが、SQL Serverの場合は、コマンドの非同期実行の仕組みがSqlCommandオブジェクトに
用意されています。System.Data.OracleClient等には、同じ機能は存在しませんのでご注意を。
非同期実行のやり方は、delegateの非同期実行のやり方と同じです。
BeginXXXXで非同期実行を開始し、EndXXXXで非同期処理を完了します。
非同期処理を行なう際の典型的なパターンは、以下のようになります。
これは、delegateの非同期実行する場合と同じです。
using(SqlCommand command = conn.CreateCommand()){ command.CommandText = "xxxxx"; // // 非同期処理を開始. // 第一引数は、非同期処理が終わった際にコールバックされるメソッド。第二引数はIAsyncResult.AsyncStateオブジェクトとして // 取得できるオブジェクト。 // IAsyncResult asyncResult = command.BeginExecuteReader(new AsyncCallback(AsyncCallbackMethod), command); } private void AsyncCallbackMethod(IAsyncResult result){ // // BeginExecuteReaderメソッドに渡したCommandオブジェクトを取得. // (EndExecuteReaderを呼ぶために必要) // SqlCommand command = null; try{ command = (SqlCommand) result.AsyncState; // // 非同期処理を完了し、データを読み取る. // using(SqlDataReader reader = command.EndExecuteReader(result)){ // // 読み取り処理・・・・ // } }finally{ if(command != null){ command.Dispose(); } } }
また、C# 2.0からは匿名デリゲートが利用できるので上のものは以下のようにも
書けます。
using(SqlCommand command = conn.CreateCommand()){ command.CommandText = "xxxxx"; IAsyncResult asyncResult = command.BeginExecuteReader( delegate(IAsyncResult result){ SqlCommand command = null; try{ command = (SqlCommand) result.AsyncState; using(SqlDataReader reader = command.EndExecuteReader(result)){ // // 読み取り処理・・・・ // } }finally{ if(command != null){ command.Dispose(); } } } ,command ); }
以下サンプルです。
今回は、以下のテーブルが存在するとします。
create table AdoNetSample008Table( id int identity primary key ,name varchar(50) not null ,age int ,address varchar(200) ,inserted datetime ,updated datetime );
using System; using System.Data; using System.Data.Common; using System.Diagnostics; using System.Transactions; using System.Threading; using NUnit.Framework; using Gsf.Lib.UnitTests; namespace Gsf.Samples.AdoNet { [TestFixture()] public class AdoNetSample008 : TestBase{ [Test()] public void Commandの実行を非同期で行なってみる(){ using(DbConnection conn = DbProviderFactories.GetFactory(_settings.ProviderName).CreateConnection()){ conn.ConnectionString = _settings.ConnectionString; conn.Open(); // // テスト用のデータをInsert. // Debug.WriteLine("準備用データの書き込み開始。"); TruncateTable(conn, "AdoNetSample008Table"); using(TransactionScope tx = new TransactionScope()){ PrepareDataInsert(conn); tx.Complete(); } Debug.WriteLine("準備用データの書き込み終了。"); // // コマンド生成及び発行 // using(DbCommand command = conn.CreateCommand()){ command.CommandText = "select id, name, age, address, inserted, updated from AdoNetSample008Table order by id"; IAsyncResult asyncResult = *1 { int count = 0; if(reader.HasRows){ foreach(DbDataRecord record in reader){ count++; } } Debug.WriteLine(string.Format("[非同期処理] 取得件数:{0}件", count)); } }finally{ if(sqlCommand != null){ sqlCommand.Dispose(); } } Debug.WriteLine("[非同期処理] データの読み取りが完了しました。"); } ,command ); // // 非同期で読み込み処理を実行し、その間待ち合わせする。 // for(int i = 0; i < 5; i++){ Debug.WriteLine("待ち合わせ中・・・・・"); Thread.Sleep(50); } Debug.WriteLine(string.Format("IAsyncResult.IsCompletedの値={0}", asyncResult.IsCompleted)); } } } private void PrepareDataInsert(DbConnection conn){ using(DbCommand command = conn.CreateCommand()) { command.CommandText = "insert into AdoNetSample008Table (name, age, address, inserted, updated) values (@Name, @Age, @Address, @Inserted, @Updated)"; DbParameter nameParam = command.CreateParameter(); nameParam.ParameterName = "@Name"; DbParameter ageParam = command.CreateParameter(); ageParam.DbType = System.Data.DbType.Int32; ageParam.ParameterName = "@Age"; DbParameter addressParam = command.CreateParameter(); addressParam.ParameterName = "@Address"; DbParameter insertedParam = command.CreateParameter(); insertedParam.DbType = DbType.DateTime; insertedParam.ParameterName = "@Inserted"; DbParameter updatedParam = command.CreateParameter(); updatedParam.DbType = DbType.DateTime; updatedParam.ParameterName = "@Updated"; command.Parameters.AddRange(new DbParameter[] { nameParam, ageParam, addressParam, insertedParam, updatedParam }); for(int i = 0; i < 10000; i++) { command.Parameters["@Name"].Value = string.Format("gsf_zero{0}", i); command.Parameters["@Age"].Value = i; command.Parameters["@Address"].Value = string.Format("address_{0}", i); command.Parameters["@Inserted"].Value = DateTime.Now; command.Parameters["@Updated"].Value = DateTime.Now; command.ExecuteNonQuery(); } } } } }
実行してみると、非同期処理は完了しているはずなのに、最後に出力するIAsyncResult.IsCompletedの値が
Falseのままになってしまいます。何故だろ・・・。とりあえず、コールバックは正常に呼ばれているので
オッケイですが。
でも、実際非同期で処理する場合は、delegateを使うかBackgroundWorkerでやったり
する方が楽なのではないでしょうか。今回のものは知識として覚えておく程度でいいと思います。
*1:System.Data.SqlClient.SqlCommand)command).BeginExecuteReader( delegate(IAsyncResult result){ Debug.WriteLine("[非同期処理] 非同期処理が完了しました。データの読み取りを行ないます。"); System.Data.SqlClient.SqlCommand sqlCommand = null; try{ sqlCommand = (System.Data.SqlClient.SqlCommand)result.AsyncState; using(DbDataReader reader = sqlCommand.EndExecuteReader(result