2009-02-25 15 views
39

私はNullable ParentIdフィールドを持つCategoryエンティティを持っています。以下のメソッドが実行されていて、categoryIdがnullの場合、nullのように見えますが、nullのParentId値を持つカテゴリがあります。Linqのnull可能な型とSqlを比較します

ここで問題は何ですか、私は何が欠けていますか?ところで

public IEnumerable<ICategory> GetSubCategories(long? categoryId) 
{ 
    var subCategories = this.Repository.Categories.Where(c => c.ParentId == categoryId) 
     .ToList().Cast<ICategory>(); 

    return subCategories; 
} 

私は(c.ParentId == nullの)に条件を変更すると、結果が通常のようです。

+1

私は方法を見つけました...更新します... –

答えて

27

最初に行うことは、生成されたTSQLを確認するためにログを記録することです。例えば:

ctx.Log = Console.Out; 

LINQツーSQLは、(値対リテラルに応じて)少し矛盾ヌルを治療するためのようだ:

using(var ctx = new DataClasses2DataContext()) 
{ 
    ctx.Log = Console.Out; 
    int? mgr = (int?)null; // redundant int? for comparison... 
    // 23 rows: 
    var bosses1 = ctx.Employees.Where(x => x.ReportsTo == (int?)null).ToList(); 
    // 0 rows: 
    var bosses2 = ctx.Employees.Where(x => x.ReportsTo == mgr).ToList(); 
} 

だから私はお勧めすることができ、すべてがNULLでトップフォームを使用しています! -

すなわち

Expression<Func<Category,bool>> predicate; 
if(categoryId == null) { 
    predicate = c=>c.ParentId == null; 
} else { 
    predicate = c=>c.ParentId == categoryId; 
} 
var subCategories = this.Repository.Categories 
      .Where(predicate).ToList().Cast<ICategory>(); 

アップデートは、私はそれがカスタムExpressionを使用して "適切に" 取り組んだ:

static void Main() 
    { 
     ShowEmps(29); // 4 rows 
     ShowEmps(null); // 23 rows 
    } 
    static void ShowEmps(int? manager) 
    { 
     using (var ctx = new DataClasses2DataContext()) 
     { 
      ctx.Log = Console.Out; 
      var emps = ctx.Employees.Where(x => x.ReportsTo, manager).ToList(); 
      Console.WriteLine(emps.Count); 
     } 
    } 
    static IQueryable<T> Where<T, TValue>(
     this IQueryable<T> source, 
     Expression<Func<T, TValue?>> selector, 
     TValue? value) where TValue : struct 
    { 
     var param = Expression.Parameter(typeof (T), "x"); 
     var member = Expression.Invoke(selector, param); 
     var body = Expression.Equal(
       member, Expression.Constant(value, typeof (TValue?))); 
     var lambda = Expression.Lambda<Func<T,bool>>(body, param); 
     return source.Where(lambda); 
    } 
+2

これを処理するには良い方法はないようです。ありがとう! –

+2

私はまったく同じ問題に遭遇し、同じ回避策を講じ、より良い方法があるかどうか尋ねようとしていました。 この動作は直感的ではありません。 –

+2

リテラルと変数の不一致が直感的ではないほど悪いと私は言いたいのですが、私の疑惑を確認してくれてありがとう+1 – Jodrell

5

私の推測では、それが原因DBMSののではなく、共通の属性にあるということです - ちょうど2つのものが両方ともヌルであるということは、それらが等しいことを意味しません。

ビットを詳述すると、これら2つのクエリを実行してみてください!

SELECT * FROM TABLE WHERE field = NULL 

SELECT * FROM TABLE WHERE field IS NULL 

「NULL IS」構文理由は、DBMSの世界では、NULL = NULL NULLの意味があるので、その値は未定義です。 NULLは定義されていないことを意味するので、2つのNULL値が等しいと言うことはできません。なぜなら、定義上、それらが何であるかを知らないからです。

"field == NULL"を明示的にチェックすると、LINQはおそらく "field IS NULL"に変換します。しかし、変数を使用すると、私はLINQが自動的にその変換を行わないと推測しています。

ここにはan MSDN forum postがあり、この問題に関する詳細情報があります。

は「カンニング」良いが、このように見えるためにあなたのラムダを変更することであるように見える:

c => c.ParentId.Equals(categoryId) 
+1

set ansi nullsスイッチによってNULL = NULLの動作をMSSQLで変更することができます参照:http://msdn.microsoft.com/en- us/library/aa259229(SQL.80).aspx –

+0

いいえ!まだ何も! :/ –

+4

あなたはobject.Equals(a、b)を使う必要があります.e.Equals(b)はしませんでした –

1

何、このような単純なものでしょうか?

public IEnumerable<ICategory> GetSubCategories(long? categoryId) 
{ 
    var subCategories = this.Repository.Categories.Where(c => (!categoryId.HasValue && c.ParentId == null) || c.ParentId == categoryId) 
     .ToList().Cast<ICategory>(); 

    return subCategories; 
} 
52

その他の方法:

Where object.Equals(c.ParentId, categoryId) 

または

Where (categoryId == null ? c.ParentId == null : c.ParentId == categoryId) 
+0

これはうまくいきました –

+0

これは私にとって完璧に機能します。ですから、私たちの述語では "=="ではなく、Equals(x、y)を使用するのがデフォルトですか、それともEqualsで他の問題がありますか? – AngieM

6

あなたは演算子を使用する必要は等しい:

var subCategories = this.Repository.Categories.Where(c => c.ParentId.Equals(categoryId)) 
     .ToList().Cast<ICategory>(); 

がNULL可能なタイプが再 FOT等しいです点灯した場合:

  • hasValueはプロパティがfalseの場合、および他のパラメータがnullです。つまり、2つのNULL値は定義によって等しくなります。
  • HasValueプロパティがtrueで、Valueプロパティによって返された値が他のパラメータと等しい場合。

と場合を返します。現在のNullable構造用hasValueはプロパティがtrueで、他のパラメータがnullの

  • 現在のNullable構造体のHasValueプロパティはfalseで、その他のパラメータはnullではありません。
  • 現在のNullable構造体のHasValueプロパティはtrueであり、Valueプロパティによって返された値は他のパラメータと等しくありません。ここ

詳細情報Nullable<.T>.Equals Method

+0

これは正解です – tggm

+1

私はこれをLinqPadでテストしましたが、これはうまくいかないようです。 'null'リテラルを渡すと、sqlが生成したCategories.ParentIDは期待通りにNULLになります。しかし変数を渡すと、Categories.ParentID = p0がテストされます.p0がnullの場合は動作しません。 @arielからのobject.Equals(Categories.ParentID、value)のアプローチは素晴らしいものでした。 –

1

それとも、単にこれを使用することができます。また、エンティティへのよりよいSQLクエリ

Where((!categoryId.hasValue && !c.ParentId.HasValue) || c.ParentId == categoryId) 
0

のLINQに翻訳しますヌルCoelescingが(??)ので、単にデフォルト値にその場でヌルを変換をサポートしています。

Where(c => c.ParentId == categoryId ?? 0) 
関連する問題