LINQtoSQLでのSELECT順

解決


ひぃ  2008-06-17 09:21:22  No: 144832  IP: 192.*.*.*

こんにちわ。
別掲示板で以前より質問を投げておりましたが、
全く返答がなかった為、こちらにも質問させて頂きます。
どちらかで何らかの回答が得られた場合は、
両方にご報告をさせて頂きますのでマルチお許し下さい。

現在の開発環境は、VB.NET2008+SQLServer2005です。

AccessVBAで作成されていたプログラムを、
完全新規で.NET版にシステムリプレイス中で、
LINQtoSQLを利用して開発を行っております。

現在悩んでおりますのは、LINQtoSQLのSELECT順についてです。

フォーム上にDataGridViewを配置(データソース無し)し、
複数のテーブルからデータを結合、必要なデータのみをDataGridViewに表示させる。

というところまでは出来たのですが、
LINQtoSQLにて指定したSELECT順の通りにDataGridViewに表示されません。


現在のソースをからの抜粋は出来ないので、
新規プロジェクトに同じようなソースを用いました。
そのテストソースを添付させて頂きます。

    Private Sub frmTest_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Dim dtc = New dcTestCheck

        Dim var = From SitenMei In dtc.SitenTbl, KaMei In dtc.KaTbl, test In dtc.KaTbl _
                   Where SitenMei.SitenCD = KaMei.SitenCD _
                   Order By SitenMei.SitenCD , KaMei.KaCD_
                   Select SitenMei.SitenName, SitenMei.SitenCD , KaMei.KaName, KaMei.KaCD

        Me.DataGridView1.DataSource = var

    End Sub

という感じでSELECTにて取得順序を指定していますが、
実際にフォームで表示させてみると、
SitenMei.SitenCD, SitenMei.SitenName, KaMei.KaName, KaMei.KaCD
となってしまっています。

いろいろ調べてみたのですが、
LINQtoSQLにて生成されるSQLでは指定した順序で取得してきているようです。
DataSourceに直接放り込むのが良くないのか?と思い、
ソースにてBindingSourceを用意して放り込んでみましたが、
BindingSourceに入った時点で順序がおかしくなっているようです。

※クイックウォッチで見てみると、BindingSourceの中のDataSourceの中に、
  結果というツリーがあって各レコード情報が表示されました。
  結果ツリー配下に(0)(1)(2)....という各レコードの詳細が見れるのですが、
  (0)という所を見てみると、
  {SitenMei = 東京支店,SitenCD = 0001,KaName = 営業課, KaCD = 01}
  という具合にSELECTで指定した順で表示されているのですが、
  (0)は更にツリーになっており、それを開いてみると、
  SitenCD    | "0001"
  SitenName  | "東京支店"
  KaCd       | "01"
  KaName     | "営業課"
  という具合になっており、これが横並びになった状態、
  つまり、実際にDataGridViewに表示させると、
  SitenMei.SitenCD, SitenMei.SitenName, KaMei.KaCD, KaMei.KaName
  になっていました。
  
何か良い方法はないものでしょうか。。。

編集 削除
シャノン  2008-06-17 10:18:56  No: 144833  IP: 192.*.*.*

DataGridView の Column の DataPropertyName を適切に設定してやればいいんじゃないですか?

編集 削除
ガッ  2008-06-17 10:29:53  No: 144834  IP: 192.*.*.*

とりあえず同じ現象に悩んでいる人URL:
http://forums.msdn.microsoft.com/ja-JP/vblanguage/thread/9e83714e-ee4b-44f3-ac6e-1d5ce0a3f98e/

select new with .. を使えばどうかとか,匿名型の要素に名前をつけたらどうかとか言ってる気がします.
が,結局未解決?
URLの最後のレスで参考サイトが挙げられているので調べてみてはいかがでしょうか.

編集 削除
ひぃ  2008-06-17 14:52:23  No: 144835  IP: 192.*.*.*

シャノン様、ガッ様  ご返信ありがとうございます。

お教えいただいたURLを翻訳して読んでみましたが、
簡単に設定する方法は結局わかりませんでした。
ですが、いろいろやっている内に、
今、私が発見出来ているやり方で一番効率的なやり方を発見しましたので、
ご報告させて頂きます。

シャノン様の
>DataGridView の Column の DataPropertyName を適切に設定する。
と、
ガッ様の
>匿名型の要素に名前をつけたらどうか

より、ヒントを頂いて作成できました。

お客さんの依頼の重要項目に、
「後々にカスタマイズが発生した場合、
  開発者でなくても簡単にカスタマイズ出来るような作りにして欲しい」
というのがありましたので、
今回は、Stringの配列を利用して、そこだけ変更すればOKなように作ってみました。

dim strColNM() As String = { "支店名", "支店コード", "課名", "課コード"}

For intCnt = 0 to (strColNM.Count - 1)
    Me.DataGridView.Colmuns.Add( strColNM(intCnt), strColNM(intCnt))
    Me.DataGridView.Colmuns(intCnt).DataPropertyName = strColNM(intCnt)
Next

Dim dtc As New dcTestCheck

Dim var = From SitenMei In dtc.SitenTbl, KaMei In dtc.KaTbl _
          Where SitenMei.SitenCD = KaMei.SitenCD _
          Order By SitenMei.SitenCD , KaMei.KaCD_
          Select 支店名 = SitenMei.SitenName _
               , 支店コード = SitenMei.SitenCD _
               , 課名 = KaMei.KaName _
               , 課コード = KaMei.KaCD

Me.DataGridView.DataSource = var

フィールド名を配列に入れないといけないのは面倒ですが、
なんとか綺麗に収まったかな?と思っています。
クエリデータにフィールド名を特定出来るようなプロパティがあれば(あるのかも知れませんが)
配列も無しに出来るような気がします。

一先ずやりたい事は出来ましたので解決とさせていただきます。
ありがとうございました。

編集 削除
ひぃ  2008-06-24 15:06:41  No: 144836  IP: 192.*.*.*

既に解決済ですが、書き込みさせて頂きましたのには理由があります。
「解決」の書き込みの内容ではまだ解決出来ていなかったからです。
(自分としては解決出来たつもりでいましたが、テストを実施していたら不具合が見つかりました)
完全解決ではありませんが、逃げ道を見つけましたのでご報告させて頂きます。
(少々長くなりますのでご興味がある方のみお読み頂くか、LINQ開発する時の参考にして頂ければ幸いです)


前回書き込みさせて頂いたソースでは、DataPropertyNameを設定すれば可能
というような感じになっていましたが、それだけでは実現出来ませんでした。
DataPropertyNameを正しく設定すれば、その行に値は入るのですが、
当初の問題であった、「SELECT順の通りに並ばない」が実現出来ていませんでした。
(フィールド数が多かったので、少し確認して「出来た」と思い違いしていました)
当初質問させて頂いていた状態での結果と、
DataPropertyNameを設定してからの結果では並び順は少し変わっていましたが、
やはりこちらが指定した通りのSELECT順にはなっていませんでした。

例えば、以下のような2つのテーブルがあったとします。
テーブル名:Siten_Table AS Siten
フィールド名:A_SitenCD,B_SitenMei,C_SitenChoCD,D_SitenChoMei
テーブル名:Ka_Table AS Ka
フィールド名:A_SitenCD_Ka,B_KaCD,C_KaMei,D_KaChoCD,E_KaChoMei
(並び順がどうなるか分かりやすいように頭をABCにしています)

このテーブルをLINQの結合で取得したとします。
当初の状態(DataPropertyNameに設定なし)で実行すると、
実際にDataGridViewに表示される順は、
Siten.A_SitenCD, Ka.A_SitenCD_Ka, Ka.B_KaCD, Siten.B_SitenMei, 
Ka.C_KaMei, Siten.C_SitenChoCD, Ka.D_KaChoCD, Siten.D_SitenChoMei, Ka.E_KaChoMei
というように、完全にABC順に並んでしまいます。
(実際にこういうテーブルを用意して試したわけではありませんが、
  私が使っているテーブルでは完全ABC順となりました)

では、DataPropertyNameに設定有で実行すると、
実際にDataGridViewに表示される順は、
Siten.A_SitenCD, Siten.B_SitenMei, Siten.C_SitenChoCD, Siten.D_SitenChoMei,
Ka.A_SitenCD_Ka,Ka.B_KaCD,C_KaMei,Ka.D_KaChoCD,Ka.E_KaChoMei
というように、テーブル単位でABC順に並んでいました。
恐らく、結合順序によりこの逆の状態にもなるのではないかと思います。

原因を調べる為にステップ実行でいろんなプロパティを見ていたのですが、
データソース=LINQ結果が行われた瞬間に変更される値を見つけました。
DataGridViewが所有する「DisplayIndex」というプロパティでした。
「解決」で記述しましたソース

dim strColNM() As String = { "支店名", "支店コード", "課名", "課コード"}

For intCnt = 0 to (strColNM.Count - 1)
     Me.DataGridView.Colmuns.Add( strColNM(intCnt), strColNM(intCnt))
     Me.DataGridView.Colmuns(intCnt).DataPropertyName = strColNM(intCnt)
Next

Dim dtc As New dcTestCheck

Dim var = From SitenMei In dtc.SitenTbl, KaMei In dtc.KaTbl _
           Where SitenMei.SitenCD = KaMei.SitenCD _
           Order By SitenMei.SitenCD , KaMei.KaCD_
           Select 支店名 = SitenMei.SitenName _
                , 支店コード = SitenMei.SitenCD _
                , 課名 = KaMei.KaName _
                , 課コード = KaMei.KaCD

Me.DataGridView.DataSource = var

↑の「Me.DataGridView.DataSource = var」が実行されるまでの
「DisplayIndex」はColmuns.Addした順になっているのですが、
「Me.DataGridView.DataSource = var」が実行されると、
「DisplayIndex」の中身が変更される。という現象が起きます。

おそらくLINQの仕様なのではないかと思いますが、
LINQの結合で取得した結果(ウォッチによるとデータベース クエリという名前らしいです)
の中身が既にABC順となっていました。
それをDataGridViewのデータソースに入れて表示させると、順番がバラバラとなり、
DataPropertyNameを設定していない場合は完全ABC順
DataPropertyNameを設定した場合でもテーブル単位でABC順
に並び変わってしまうようです。

これを回避するには、「DisplayIndex」を当てなおすしかありません。
(もしくはLINQのクラスに必要なフィールドを順番通りに抜き出したクエリ用のClassを作成して、
  それをデータソースとしてDataGridとして表示させる。でしょうか?
  試していないので出来るかどうかはわかりませんが)
私は、お客様の依頼により、
「デザイナで全部出来るならデザイナでやって欲しいが、
  それが無理ならば、逆にデザイナはあまり使わず、
  ソースでここだけ直せば大丈夫。という状態にして欲しい」
という事でしたので、DisplayIndexを当て直す事にしました。

以下のようなソースになります。

dim strColNM(,) As String = {{0,"支店名"}, {1,"支店コード"}, {2,"課名"}, {3,"課コード"}}

For intCnt = 0 to strColNM.GetUpperBound(0)
     Me.DataGridView.Colmuns.Add( strColNM(intCnt,1), strColNM(intCnt,1))
     Me.DataGridView.Colmuns(intCnt).DataPropertyName = strColNM(intCnt,1)
Next

Dim dtc As New dcTestCheck

Dim var = From SitenMei In dtc.SitenTbl, KaMei In dtc.KaTbl _
           Where SitenMei.SitenCD = KaMei.SitenCD _
           Order By SitenMei.SitenCD , KaMei.KaCD_
           Select 支店名 = SitenMei.SitenName _
                , 支店コード = SitenMei.SitenCD _
                , 課名 = KaMei.KaName _
                , 課コード = KaMei.KaCD

Me.DataGridView.DataSource = var

'データソースに入れた後、DisplayIndexを当て直す(LINQ仕様回避)
For intX = 0 to (Me.DataGridView.ColmunCount - 1)
    For intY = 0 to strColNM.GetUpperBound(0)
        If Me.DataGridView.Colmuns(intX).Name = strColNM(intY,1) Then
            Me.DataGridView.Colmuns(intX).DisplayIndex = strConNM(intY,0)
        End IF
    Next intY
Next intX


かなり逃げなソースですが、
配列に指定した数値通りの順に行を並べる事が出来ました。

長々と書き込み申し訳ありませんでした。
どなたかの参考になれば幸いです。

編集 削除