none
LinQ to Entities のWhere句の書き方について

    質問

  • ASP.net 4.5で、Webフォーム(V)からIDを指定して検索する機能の実装で、

    テーブル(M)OracleNumber9,0)型で定義されている項目を条件とする場合の、

    LinQ to Entities Where句の書き方がわからず困っています。

    VarChar型の項目NAMEについては、下記で正常に検索できました。

    from M in OracleDBM _

    where M.NAME.Equals(V.NAME) _     ← テキストボックス

    select M.ID,M.Name

    これを踏まえ、where M.ID.Equals(V.ID) _

    としたところ「追加情報: 'System.Object' の定数値を作成できません。このコンテキストでサポートされるのはプリミティブ型または列挙型だけです。」

    とのエラーで、型が違うからだろうと、

    テキストボックスの値をあらかじめIntegerCastしたところ

    where V.ID= ID_INT で正常に動作しました。

    しかし①複数の検索条件を一つのLinq構文で実現したい、

    ②項目が空白(NAMEのみで検索など)を考慮したり、DB

    の項目的にもNULLを許容したりする必要性から、Linq 構文内

    Castを実現したいと考え、色々と試みましたが、

    LINQ to Entities tostringは使えない、

    SqlFunctions.StringConvertを試そうと、Imports System.Data.SqlClientを定義するが、

    「名前 'SqlFunctions' は宣言されていないか、または現在のスコープ内に存在しません。」

    というエラーになります。入力候補一覧でも出てきません。

    Castのよい実現方法あるいは、Number型をWhere条件と場合のよい方法がありましたら、ご教授願います。

    --(参考)--------------

    テキストボックスの値は、ソース上で検索条件一式を区別しやすいよう、クラスのプロパティに一旦格納しています。

    Private Class SearchItem

            Property ID As String

            ・・・

    End Class

    Dim V = New SearchItem

    V.ID= ID.Text (テキストボックスの値)

    ------------------------

    2015年7月24日 5:11

回答

  • まずLinq抜きの素のSQL操作で考えてみて、
    テーブルに対するクエリでカラム側をキャストするのは筋悪でしょう。インデックス効かなくなりますし。
    どうしてもじゃなければパラメータの方をカラムに揃えてあげた方が良いと思います。

    質問に書かれているやりたいことも、べつに特殊なとこではなくてよくあるケースだと思います。
    条件によってWhere句を変えたいなら

    if (V.ID != null) //数値チェックは省略
    {
    	query = query.Whrere(m => m.ID == (int)V.ID);
    }
    
    の様に書けば、条件に合った時だけWhere句が追加されます。

    Linq to EntitiesをRDBに対して使う時は、APサーバーで処理できることは処理して(この場合はパラメータの入力有無チェック)からDBサーバーに渡してあげるのがセオリーかなと思います。
    入力チェックや複雑なロジックをDBサーバーにやらせたいのなら、Linqは使わないか、使ってもExecuteQueryするのがベターじゃないですかね。

    • 回答の候補に設定 星 睦美 2015年7月27日 4:31
    • 回答としてマーク takusan 2015年7月28日 3:40
    2015年7月25日 17:56

すべての返信

  • <追記になります>

    DB⇔Entity間の型は、モデルブラウザのマッピングで、ID:number⇔ID:Int32 が出来ている前提になります。

    2015年7月24日 5:34
  • まずLinq抜きの素のSQL操作で考えてみて、
    テーブルに対するクエリでカラム側をキャストするのは筋悪でしょう。インデックス効かなくなりますし。
    どうしてもじゃなければパラメータの方をカラムに揃えてあげた方が良いと思います。

    質問に書かれているやりたいことも、べつに特殊なとこではなくてよくあるケースだと思います。
    条件によってWhere句を変えたいなら

    if (V.ID != null) //数値チェックは省略
    {
    	query = query.Whrere(m => m.ID == (int)V.ID);
    }
    
    の様に書けば、条件に合った時だけWhere句が追加されます。

    Linq to EntitiesをRDBに対して使う時は、APサーバーで処理できることは処理して(この場合はパラメータの入力有無チェック)からDBサーバーに渡してあげるのがセオリーかなと思います。
    入力チェックや複雑なロジックをDBサーバーにやらせたいのなら、Linqは使わないか、使ってもExecuteQueryするのがベターじゃないですかね。

    • 回答の候補に設定 星 睦美 2015年7月27日 4:31
    • 回答としてマーク takusan 2015年7月28日 3:40
    2015年7月25日 17:56
  • いままで VB.NET の遅延バインディング等に頼って、型をあまり意識しないでプログラムを書いてきたのでしょうか?

    もしそうなら、今回のケースで有効かどうかは分かりませんが、今後は Option Strict On に設定してプログラミングすることをお勧めします。

    Option Strict ステートメント
    https://msdn.microsoft.com/ja-jp/library/zcd4xwzs.aspx

    2015年7月26日 12:54
  • アドマイヤコジーンさん

    ご回答ありがとうございます。
    Linq の評判に、かなり幻想を抱いていたようです。
    型を意識しないで、SQLのようにnull判定も含めて1行で様々なことが表現
    できるのでは、と誤解していて、こちらが知らない書き方があるのではないか
    と質問しました。

    ご指摘の通り、パフォーマンスもある程度妥協して、可読性を検討していました。
    結局、ExecuteQueryを使ったほうがベターという結論になりそうです。
    ありがとうございました。

    2015年7月27日 0:37
  • SurferOnWwwさん

    ご回答ありがとうございます。

    既存システムの解析からスタートし、元が無かったので、特に思慮もなく
    Option Strict 無しで再構築スタートしました。

    むしろ遅延バインディングも積極的に使い、コーディング削減、メンテナンス性
    (特に後々の可読性)を優先させようとしましたが、Linqに期待し過ぎていた感
    が否めず、Option Strict onも含め、再検討したいと思います。

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


    2015年7月27日 0:48
  • > むしろ遅延バインディングも積極的に使い、コーディング削減、メンテナンス性
    >(特に後々の可読性)を優先させようとしましたが、

    コーディング削減には多少の効果はあるかもしれませんが、分かりにくいバグを生む元になり、開発工数の削減、保守性の向上には逆効果だと思います。

    VB は、あまりプログラムに関する知識がない人が、型を強く意識しなくても、より容易にプログラムが作成できるよう配慮されており、それが逆に分かりにくいバグを生むといった問題の元になっています。

    C# ではコンパイルエラーになって実行前に容易に修正できる型の間違いが、VB.NET の Option Strict Off(デフォルトでこれのはず)では見つからず、デバッグ時もしくはもっと後のリリース後に問題が出るということがあるはずです。

    というわけで、Option Strict On をお勧めしたのです。そうすれば、イヤでも型を強く意識せざるを得ません。

    最初に質問にあった M.ID.Equals(V.ID) は間違い(M.ID は Int32 型、V.ID は String 型)というのはコーディングする前にも分かるはずです。

    > Linqに期待し過ぎていた感
    > が否めず、Option Strict onも含め、再検討したいと思います。

    LINQ では型を意識しないでいい訳ではないですよ。

    LINQ は Microsoft の MSDN ライブラリによると ".NET Framework に追加された汎用クエリ機能" ということで、普通の SQL が、C# や VB.NET のプログラム内では単なる文字列として扱われるのに対し、LINQ はそれ自体が .NET Framework の言語として認識されます。

    なので、.NET Framework の型を意識してコーディングするということになるはずです。

    LINQ でも VB.NET の初心者に便宜を図っている特別な機能に頼るというのは、私の個人的意見を言わせていただければ、邪道です。

    2015年7月27日 1:20