Backgroundworkerでデータセット取得について

解決


fumofumo  2010-06-17 11:11:12  No: 146919  IP: 192.*.*.*

環境: Windows XP(SP3)、VB2005 、  Oracle9i

Oracle からのデータ取得処理を
バックグラウンドスレッドで行い
DataGridView に表示させたいのですが
うまくいきません
バッググラウンドスレッドで取得した
データセットをどのように取得すればよいか
ご教授願います

-- データ表示ボタン処理 --
Private Sub BtnHyouji_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BtnHyouji.Click
    Call BackgroundWorker1.RunWorkerAsync()
    DataGridView1.DataSource = J20_DataSet.Tables("J20F")
End Sub

-- バッググラウンド処理 --
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
    Call Set_SQL_01()        --SQL文作成
    OraCmd.CommandText = Str_Sql
    OraDataAdp = New OracleDataAdapter(OraCmd)
    OraDataAdp.Fill(dTst, "J20F")     --データ取得 
    e.Result = dTst  (Private dTst as New DataSetをFormで宣言済)
End Sub

-- メインスレッドのデータセットにデータを代入 --
Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted
    J20_DataSet.Clear()
    J20_DataSet = e.Result
End Sub

編集 削除
魔界の仮面弁士  2010-06-17 12:02:23  No: 146920  IP: 192.*.*.*

RunWorkerCompleted では、J20_DataSet そのものを差し替えるのではなく、
J20_DataSet 内に取得結果を Merge するようにしてみてください。

編集 削除
fumofumo  2010-06-17 13:39:26  No: 146921  IP: 192.*.*.*

魔界の仮面弁士さん
ありがとうございます。

RunWorkerCompleted 内を

    J20_DataSet.Clear()
    J20_DataSet.Dispose()
    J20_DataSet = New DataSet        --FormLoadでセットした構成との競合を防ぐため(Merge時)
    J20_DataSet.Merge(e.Result)

としたらJ20_DataSetにデータは入っている
ようなのですが、よくわからない現象が起きました。

データ表示ボタンの処理で
Call BackgroundWorker1.RunWorkerAsync()
の後、DoWorkイベントが走らずに
DataGridView1.DataSource = J20_DataSet.Tables("J20F")
が走った後に、DoWorkイベントが実行されます

何か設定が間違っているのでしょうか?
ちなみに BackgroundWorker1のプロパティは
GenerateMenber               True
Modifiers                    Friend
WorkerReportsProgress        True
WorkerSupportsCancellation   False

となっています。

編集 削除
魔界の仮面弁士  2010-06-17 13:46:16  No: 146922  IP: 192.*.*.*

> e.Result = dTst  (Private dTst as New DataSetをFormで宣言済)
この dTst は、他から利用される物ではありませんので、
Form レベルのフィールド変数として宣言するのではなく、
DoWork 内で Dim を使って宣言しましょう。メインスレッド側では、
それを RunWorkerCompleted の e.Result 経由で受け取れます。


> Call Set_SQL_01()        --SQL文作成
> OraCmd.CommandText = Str_Sql
Str_Sql の宣言が無いことなどから、恐らくは、共通変数 Str_Sql を Set_SQL_01 内で
書き換えているのだと推測しますが、そのような設計は避けるべきです。

たとえば、ワーカースレッド(DoWork)内で Str_Sql を操作している最中に、
メインスレッド(Button_Click 等)でも Str_Sql が書き換えた場合、
意図していた文字列とは異なる情報を受け取ってしまうかもしれません。

この場合は、SQL を Function の戻り値で返すようにして
  Dim sql As String = Get_SQL_01()
  OraCmd.CommandText = sql
のようなコードにした方が安全でしょう。Set_SQL_01 の処理内容次第ではありますが。

編集 削除
魔界の仮面弁士  2010-06-17 13:48:23  No: 146923  IP: 192.*.*.*

> Call BackgroundWorker1.RunWorkerAsync()
> の後、DoWorkイベントが走らずに
> DataGridView1.DataSource = J20_DataSet.Tables("J20F")
> が走った後に、DoWorkイベントが実行されます

何か都合が悪いのでしょうか?

非同期処理ですから、RunWorkerAsync 呼び出し直後に処理されようと、
End Sub 実行後に処理されようと、特に問題は無い気がしますが…。

編集 削除
fumofumo  2010-06-17 14:24:13  No: 146924  IP: 192.*.*.*

魔界の仮面弁士さん
ありがとうございます。

>この dTst は、他から利用される物ではありませんので、
>Form レベルのフィールド変数として宣言するのではなく、
>DoWork 内で Dim を使って宣言しましょう。メインスレッド側では、
>それを RunWorkerCompleted の e.Result 経由で受け取れます。

ご指摘ありがとうございます。
Form レベルで宣言していたら後述の Str_Sql と同じ事が
起こりうるからですね。
DoWork 内で Dim で宣言します。

>Str_Sql の宣言が無いことなどから、恐らくは、共通変数 Str_Sql を Set_SQL_01 内で
>書き換えているのだと推測しますが、そのような設計は避けるべきです。

>たとえば、ワーカースレッド(DoWork)内で Str_Sql を操作している最中に、
>メインスレッド(Button_Click 等)でも Str_Sql が書き換えた場合、
>意図していた文字列とは異なる情報を受け取ってしまうかもしれません。

全くその通りです。
ワーカースレッドを後付けしたのに変更していませんでした。
変更します。

デバッグして分かったのですが、
Call BackgroundWorker1.RunWorkerAsync()
で DoWork が実行されずに
DataGridView1.DataSource = J20_DataSet.Tables("J20F")
が実行されて DataGridView1 にデータが入っていないみたいです。

画面の現象を書いていませんでした。
申し訳ないです^^;

フォーム起動後一回目に BtnHyouji_Click を実行したら
画面には何も表示されません(列ヘッダはJ20_DataSetのものが表示されています)
二回目以降は
ボタンをクリックした直後に、データが表示されて
直ぐにデータが消えて最終的に画面にデータは表示されて
いません。
もっと的確にコードを開示出来ればよいのですが、
どのコードが影響があるか分からないのが現状です。

かなり抽象的で申し訳ないのですが
何かお気づきになる点がございましたらアドバイス
お願いします。

編集 削除
fumofumo  2010-06-17 15:40:52  No: 146925  IP: 192.*.*.*

Call BackgroundWorker1.RunWorkerAsync()
 の後、DoWorkイベントが走らずに
 DataGridView1.DataSource = J20_DataSet.Tables("J20F")
 が走った後に、DoWorkイベントが実行されます

は間違いではないみたいです。

DoWork より BtnHyouji_Click イベントの方が
早く処理される為にそう見えるみたいです
(非同期処理で尚且つ BackgroundWorker スレッドに時間が掛かる為)

だから J20_DataSet にデータが入る前に
DataGridView1.DataSource = J20_DataSet.Tables("J20F")
が実行されて、何も表示されていなかったみたいです。

これは RunWorkerCompleted  内で DataGridView1.DataSource = J20_DataSet.Tables("J20F")
をすることにより解決したのですが、別の問題が発生しました。

問題:
メインスレッドで、実行される RowPostPaint や CellPainting 
イベントが、BackgroundWorker スレッドでは実行されないので
個別のセル描画が出来ないということです。(当然なのですが・・・)

RunWorkerCompleted  で RowPostPaint や CellPainting を
うまく実行することは可能でしょうか?
また、他の回避方法があればご教授願います。

編集 削除
fumofumo  2010-06-17 15:55:50  No: 146926  IP: 192.*.*.*

すみません。

RowPostPaint や CellPainting  は実行
出来ていました。
勘違いでしたm(_ _)m

こちらに書き込みすることによって
不明な部分、何が検証出来ていないか、
どういう対策が打てるか  等が
整理出来て解決できました。

不確かな書き込みでご迷惑をお掛けしたかと
思いますが、すみませんでした。

これからはもっと自分の中で整理整頓して
自己解決出来るように頑張っていきたいと
思いますので、つまづいた時はまた宜しくお願い
します。

魔界の仮面弁士さんアドバイスありがとうございました。

編集 削除