tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトル抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No5986
投稿日: 2007/08/01(Wed) 17:41
投稿者じゅん

複雑な抽出クエリやレコード数の多い抽出クエリを実行すると
"ここ" の adapter.Fill で処理が止まってしまい、画面が真っ白になってしまいます。

adapter.Fill を非同期で実行するにはどのようにすれば良いですか?
それが無理であれば、代わりとなる方法を教えていただけませんでしょうか?
(ただし、抽出クエリを実行して、 DataSet に格納したい。)

環境 Visual Basic 2005

'=========== 以下、サンプルコード =============================================

Dim ds As New System.Data.DataSet

' データベースに接続
Dim connection As New System.Data.Odbc.OdbcConnection("Driver={xxx};na=xxx;db=xxx;uid=xxx;pwd=xxx;")

Dim adapter As New System.Data.Odbc.OdbcDataAdapter("select * from table", connection)

' 抽出クエリの実行
adapter.Fill(ds)               '<--- ここ

' レコード数分ループ
For Each row As System.Data.DataRow In ds.Tables(0).Rows

    ' カラム数分ループ
    For col As Integer = 0 To ds.Tables(0).Columns.Count - 1

        ' Null を空の文字列に置き換える(下の Join でエラーが出ないようにする為)
        row(col) = row(col).ToString

    Next

    ' 1レコードのデータをカンマ区切りで連結したものをイミディエイトウィンドウに出力
    Debug.Print(Join(row.ItemArray, ","))

Next

' データベースから切断
connection.Close()

'=========== 以上、サンプルコード =============================================

[ツリー表示へ]
タイトルRe: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No5988
投稿日: 2007/08/01(Wed) 19:44
投稿者魔界の仮面弁士
> adapter.Fill を非同期で実行するにはどのようにすれば良いですか?

BackgroundWorker クラスを併用してみては如何でしょう。

[ツリー表示へ]
タイトルRe^2: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No6000
投稿日: 2007/08/02(Thu) 18:06
投稿者じゅん

> BackgroundWorker クラスを併用してみては如何でしょう。

次のようにしてみました。
とりあえず、期待通りの動作にはなったのですが、
問題個所・改良すべき個所はありますか?

BackgroundWorker クラスを使うのは初めてなもので、
よろしくお願いいたします。
(下のコードもネットからダウンロードしたものを
  自分用にアレンジしただけです・・・^^;)


'=========== 以下、サンプルコード =============================================

' Button1 と BackgroundWorker1 を追加しておく。

Dim ds As New System.Data.DataSet
Dim isComplete As Boolean

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    ' adapter.Fill が完了したかどうかのフラグ
    isComplete = False

    ' adapter.Fill を非同期で実行( adapter.Fill が完了する前に次のコードを実行させる)
    BackgroundWorker1.RunWorkerAsync()

    ' adapter.Fill が完了するまでループ(画面が真っ白にならないように DoEvents を実行しつづける)
    Do
        Application.DoEvents()
    Loop While isComplete = False

    ' レコード数分ループ
    For Each row As System.Data.DataRow In ds.Tables(0).Rows

        ' カラム数分ループ
        For col As Integer = 0 To ds.Tables(0).Columns.Count - 1

            ' Null を空の文字列に置き換える(下の Join でエラーが出ないようにする為)
            row(col) = row(col).ToString

        Next

        ' 1レコードのデータをカンマ区切りで連結したものをイミディエイトウィンドウに出力
        Debug.Print(Join(row.ItemArray, ","))

    Next

End Sub

Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    ' データベースに接続
    Dim connection As New System.Data.Odbc.OdbcConnection("Driver={xxx};na=xxx;db=xxx;uid=xxx;pwd=xxx;")

    Dim adapter As New System.Data.Odbc.OdbcDataAdapter("select * from table", connection)

    ' 抽出クエリの実行
    adapter.Fill(ds)               '<--- ここ

    ' データベースから切断
    connection.Close()

End Sub

Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    isComplete = True
End Sub

'=========== 以上、サンプルコード =============================================

[ツリー表示へ]
タイトルRe^3: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No6002
投稿日: 2007/08/02(Thu) 19:25
投稿者魔界の仮面弁士
>     Do
>         Application.DoEvents()
>     Loop While isComplete = False
このような待機処理は、基本的に行わないでください。

RunWorkerAsync 発行後、完了を待つために「ループで待機する」のではなく、
完了後に呼び出される「RunWorkerCompleted イベント」で事後の処理を行うようにします。


>     ' 抽出クエリの実行
>     adapter.Fill(ds)               '<--- ここ
DoWork イベントでの処理中には、(isComplete や ds などの)フォーム変数や
コントロールなどを操作してはいけません。
基本的には、すべてイベント内の変数だけで完結させるようにし、
進捗状況や結果などは、引数で受け渡すようにします。


概念的には、こんな感じ。

'<呼び出し>
Sub Button1_Click(・・・
   '引数情報。テーブル名でも接続文字列でもお好きなものを入れておきましょう。
  Dim arg As Object = 任意の値

  Label1.Text = "検索中"

  '非同期処理を開始するように指示するメソッド。
  BackgroundWorker1.RunWorkerAsync(arg)
End Sub

'<非同期で処理>
Sub BackgroundWorker1_DoWork(・・・
  '引数情報を受け取る。この値は好きなように使ってください。
  Dim arg As Object = e.Argument

  '*** 長い処理をここで実行 ***
  Dim dataSet As New Dataset()
  Using connection As New ・・・
   Using adapter As New ・・・
    adapter.Fill(dataSet)
     End Using
   connection.Close()
  End Using

  '処理結果を Result に代入する。(この場合は DataSet)
  e.Result = dataSet
End Sub

'<処理が終わった時に呼ばれるイベント>
Sub BackgroundWorker1_RunWorkerCompleted(・・・
  '処理結果を受け取る。(この場合は DataSet)
  ds = DirectCast(e.Result, DataSet)

  '*** 必要であれば、受け取ったデータの表示処理などをここに記述
  '(砂時計カーソルを使っていたら、それを戻したりとか)
  Label1.Text = "検索完了"
End Sub


# 実際には、RunWorkerCompleted で e.Result を受け取る前に、
# e.Cancelled や e.Error の内容を確認するようにしてください。

[ツリー表示へ]
タイトルRe^4: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No6006
投稿日: 2007/08/03(Fri) 12:19
投稿者じゅん

実際には、

Private Sub Button1_Click(...
  処理1
  抽出1
  処理2
  抽出2
  処理3
  抽出3
  処理4
End Sub

のように、何度も抽出を行い、
その都度、別の DataSet に結果を格納しております。
その為、
抽出の部分をサブプロシージャ化
したかったのですけども・・・

何か良い方法はありませんでしょうか?(´;ω;`)


以下の方法はよくないですよね・・・

Sub Button1_Click(・・・
  処理1
  BackgroundWorker1.RunWorkerAsync(arg)
End Sub

'<非同期で処理>
Sub BackgroundWorker1_DoWork(・・・
  抽出1
End Sub

'<処理が終わった時に呼ばれるイベント>
Sub BackgroundWorker1_RunWorkerCompleted(・・・
 処理2
 抽出2  <- ここの抽出で別の BackgroundWorker2 を使用する???
 処理3
 抽出3  <- ここの抽出で別の BackgroundWorker3 を使用する???
 処理4
End Sub

[ツリー表示へ]
タイトルRe^5: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No6007
投稿日: 2007/08/03(Fri) 13:10
投稿者魔界の仮面弁士
> Private Sub Button1_Click(...
>   処理1
>   抽出1
>   処理2
>   抽出2
>   処理3
>   抽出3
>   処理4
> End Sub

それぞれは、同時に並行して行える処理なのでしょうか。
それとも、順に行わなければならない処理なのでしょうか。


並行処理が可能な処理ならば、
 Sub Button1_Click(...
   BackgroundWorker1.RunWorkerAsync(引数1)
   BackgroundWorker2.RunWorkerAsync(引数2)
     :
 End Sub
のように、複数のワーカースレッドを同時に走らせれば良いかと思います。

連続した処理であるならば、
 Private Sub Button1_Click(...
   BackgroundWorker1.RunWorkerAsync(引数)
 End Sub
 Sub BackgroundWorker1_DoWork(・・・
   処理1
   抽出1
   処理2
   抽出2
    :
 End Sub
のように、ワーカースレッドは一つだけでも大丈夫かと。

[ツリー表示へ]
タイトルRe^6: 抽出クエリを非同期で実行し、 DataSet に入れる方法
記事No6014
投稿日: 2007/08/06(Mon) 12:32
投稿者じゅん
> 連続した処理であるならば、
>  Private Sub Button1_Click(...
>    BackgroundWorker1.RunWorkerAsync(引数)
>  End Sub
>  Sub BackgroundWorker1_DoWork(・・・
>    処理1
>    抽出1
>    処理2
>    抽出2
>     :
>  End Sub
> のように、ワーカースレッドは一つだけでも大丈夫かと。

!!!

この方法は気づきませんでした^^;
ボタンクリックで行う処理を全部
別スレッドで実行してしまえばいいんですね。
(引数 e.Argument や結果 e.Result の
  設定・取得が難しそうですけども。。。)

この方法でやってみようと思います。
ありがとうございました。

[ツリー表示へ]