動的LINQ への返答

投稿で使用できる特殊コードの説明。(別タブで開きます。)
本名は入力しないようにしましょう。
投稿した後で削除するときに使うパスワードです。返答があった後は削除できません。
返答する人が目安にします。相手が小学生か社会人かで返答の仕方も変わります。
最初の投稿が質問の場合、質問者が解決時にチェックしてください。(以降も追加書き込み・返信は可能です。)
※「過去ログ」について書くときはその過去ログのURLも書いてください。

以下の返答は逆順(新しい順)に並んでいます。

投稿者 CnCnSf  (社会人) 投稿日時 2016/2/15 21:58:34
 
皆さん、ありがとうございます。

------------------------------
<YuOさんへ>

教えていただいた内容が、やっと理解できました。
すいません。 色々とありがとうございました。

------------------------------
<とくまさん へ>

ご指摘/コメント ありがとうございます。

私の聞き方が悪かったです。すいません。

今回、私が聞きたかったのは、結論 (全部教えてね) でした。
この聞き方は、質問者として問題がありますが、
最低限、自分が聞きたい事は、まわりくどい言い方ではなく、正直に聞くべきでした。

質問内容に問題があり、色々と ご指摘あるかと思いますが、
とりあえず、最低限のマナーとして、
自分の聞きたい事は、まわりくどい言い方ではなく、
ストレートに、きちんと伝えるべきでした。
色々問題が多すぎだし、また不愉快な思いをさせて申し訳ありませんでした。

------------------------------
<魔界の仮面弁士さん へ>

いつも、ありがとうございます。
おかげさまで、すべて解決できました。

いつも質問者の目線で、わかりやすい解説をありがとうございます。
おかげで、すべて解決できたし、技術的にも得るものがありました。
ありがとうございました。

いただいた回答から、自分なりに色々調べてみると、
YuOさんから頂いた回答内容についても理解する事ができました。
(もちろん、YuOさんにも感謝します。ありがとうございます。今更でスイマセン)

ありがとうございました。

以上

投稿者 (削除されました)  () 投稿日時 2016/2/15 21:22:22
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/2/15 11:44:04
先の Query2 で、Predicate を使わずに単純化したものも載せておきます。

'    (   [ID] IN (     '001',    '002') )  
' OR ( [NAME] IN ('ライオン', 'コアラ') )  
' OR ( [DESC] LIKE '%長い%'             )  
'  
Dim Query2改 = From row In MstDtTbl
     Where IdList.Contains(row.Field(Of String)("ID")) _
     OrElse NameList.Contains(row.Field(Of String)("NAME")) _
     OrElse row.Field(Of String)("DESC"Like DescLike
     Select New With {
         Key .NAME = row.Field(Of String)("NAME"),
         Key .DESC = row.Field(Of String)("DESC")
     }

※ Key を付けるかどうかはお好みで。


上記の場合、ID/NAME/DESC いずれかの条件に一致したものが抽出されます。
Linq の記述は変更せず、渡すリストを
 Dim IdList As New List(Of String)() From {"001""002"}
 Dim NameList As New List(Of String)() '要素なし 
 Dim DescLike As String = "" '該当なし 

にしてやれば、ID だけでの検索となります。


条件のどこまでを動的とするのかにもよりますが、
まずは YuO さんが書かれていたように、List.Contains
あるいは Array.IndexOf を使うところから
始めてみるのが良いと思います。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/2/15 11:15:39
> そのような,単一の条件式に置き換え不可能な条件をOrElseで結合したい場合は,

試しに書いてみました。

Dim IdList   As New List(Of String)() From {     "001",    "002"}
Dim NameList As New List(Of String)() From {"ライオン""コアラ"}
Dim DescLike As String = "*長い*"


'-- 以下の問い合わせに相当-- 

'     (   [ID] IN (     '001',    '002') ) 
' AND ( [NAME] IN ('ライオン', 'コアラ') ) 

Dim Query1 = From row In MstDtTbl
             Where IdList.Contains(row.Field(Of String)("ID"))
             Where NameList.Contains(row.Field(Of String)("NAME"))
             Select NAME = row.Field(Of String)("NAME"),
                    DESC = row.Field(Of String)("DESC")


Dim conditions As New List(Of Predicate(Of DataRow))()
conditions.Add(Function(row) IdList.Contains(row.Field(Of String)("ID")))
conditions.Add(Function(row) NameList.Contains(row.Field(Of String)("NAME")))
conditions.Add(Function(row) row.Field(Of String)("DESC"Like DescLike)

'-- 以下の問い合わせに相当-- 

'    (   [ID] IN (     '001',    '002') ) 
' OR ( [NAME] IN ('ライオン', 'コアラ') ) 
' OR ( [DESC] LIKE '%長い%'             ) 

Dim Query2 = From row In MstDtTbl
             Where conditions.Any(Function(c) c(row))
             Select NAME = row.Field(Of String)("NAME"),
                    DESC = row.Field(Of String)("DESC")
投稿者 とくま  (社会人) 投稿日時 2016/2/15 10:48:26
>「 わからない、わからない 」 では ご迷惑になるので、
そういう意識を持って頂けると回答者側は助かる部分が出てくるはずですが、
結局のところどこを説明して欲しいのかというと「全部」というのでは
回答者側の負担が大きいまま全く変わっていませんよね。

まずは、タイトルにも使われている「動的LINQ」ででもWEB検索すれば、
LINQ 自体が動的に構文を変更することができないことが分かると思います。
そこで色々と工夫が必要になります。
http://www.atmarkit.co.jp/fdotnet/dotnettips/986dynamiclinq/dynamiclinq.html

そして YuO さんご提案の前半部分
>対象をList(Of String)なりStringの配列なりに入れておいて,
いわゆるコレクションと呼ばれるものに入れておいて、その内容を
LINQ の判定前に編集する…つまり直接 LINQ の構文とは関係ない
コレクションを事前に編集して、LINQ ではコレクションに含まれている
かどうかの判定だけする…くらいの解釈はプログラムの実力がかなり低くても
できそうに思いますがどうなんでしょうか?

そこまで分かっていれば、
・コレクションを使う LINQ が全く分からない
(↑これは LINQ の基本なので全く勉強していないことになりますが)
・コレクションを使う LINQ がコンパイルエラーが出る
・コレクションを使う LINQ では要件上不都合がある
くらいの具体的な説明が回答者側から出てきて欲しい所です。

何が分かって、何が分からなくて、
どこを説明して欲しいとかは具体的に説明できないのでしょうか?
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2016/2/15 09:41:11
いろいろな書き方ができるとは思いますが、一例として。

Dim IdList As New List(Of String)() From {"001""002"}

Dim Query = From row In MstDtTbl
            Where IdList.Contains(row.Field(Of String)("ID"))

Dim selTdTbl As DataTable = Query.CopyToDataTable()
selTdTbl.Columns.Remove("ID")
投稿者 CnCnSf  (社会人) 投稿日時 2016/2/14 18:01:51
 
YuOさん、ありがとうございます。

質問に言葉足らずな所がありましたので、
1つだけ補足すると、
動的に生成したい条件は、Orではなく、OrElse です。

さて、いただいた回答についてですが。
すいません、私のレベルが低すぎて、よく理解できませんでした。

「 わからない、わからない 」 では ご迷惑になるので、
YuOさんからいただいた回答の文言 一つ一つを色々調べて、
少しでも理解しようとしたのですが、ダメでした・・・。

大変申し訳ありませんが、
もう少し ご説明をいただけたら ありがたいです。

投稿者 YuO  (社会人) 投稿日時 2016/2/14 13:44:50
単純に,MstTbl.Field(Of String)("ID")が複数のうちから,というのであれば,
対象をList(Of String)なりStringの配列なりに入れておいて,
List.ContainsやArray.IndexOfであるかどうかを調べるのが簡単かと。

そのような,単一の条件式に置き換え不可能な条件をOrElseで結合したい場合は,
Predicate(Of DataRow)またはFunc(Of DataRowt, Boolean)をListや配列に入れて,
条件式を
conditions.Any(Function (cond) cond(MstTbl))
のようにするあたりが対策になるかと。
AndAlsoであれば,単純にWhere拡張メソッドをIEnumerableに追加していくだけなのですが。

なお,OrElseではなく本当にOrであるなら,
conditions.Select(Function (cond) cond(MstTbl)).ToArray().Any()
が条件式になるのですが……。
投稿者 CnCnSf  (社会人) 投稿日時 2016/2/14 13:02:59
 
いつも拝見させていただいており、
 大変お世話になっています。

 開発環境は、
VB2010(.NET Framework4.0)です。

DataTable変数からデータを抽出するLINQ文について。
このLINQ文で動的にwhere句を組み立てたいです。

たとえば、以下のLINQは、
ID列が、001 もしくは002のレコードを抽出するものですが。

このwhere句の部分を動的に組立てたいです。

具体的には、以下 LINQ文の、
MstTbl.Field(Of String)("ID") = "xxx" の条件を
動的に、ORで、n個つなげたいです。



Dim MstDtTbl As DataTable = New DataTable

MstDtTbl.Columns.Add("ID")
MstDtTbl.Columns.Add("NAME")
MstDtTbl.Columns.Add("DESC")

MstDtTbl.Rows.Add("001", "ライオン", "肉食")
MstDtTbl.Rows.Add("002", "チーター", "足早い")
MstDtTbl.Rows.Add("003", "キリン", "首が長い")
MstDtTbl.Rows.Add("004", "コアラ", "オーストラリア")
MstDtTbl.Rows.Add("005", "タカ", "空飛ぶ")

Dim Query = _
    From MstTbl In MstDtTbl.AsEnumerable() _
    Where MstTbl.Field(Of String)("ID") = "001" _
       Or MstTbl.Field(Of String)("ID") = "002" _
    Select New With _
           { _
               .NAME = MstTbl.Field(Of String)("NAME"), _
               .DESC = MstTbl.Field(Of String)("DESC") _
           }


Dim SelDtTbl As DataTable = New DataTable
SelDtTbl.Columns.Add("NAME")
SelDtTbl.Columns.Add("DESC")

For Each Qry In Query
    SelDtTbl.Rows.Add(Qry.NAME, Qry.DESC)
Next