要するに問題
where句で使用されるラムダ式がありますが、これは「期待される」結果を返していません。
クイックサマリー
analysisObjectRepositoryオブジェクトには、Parentという名前のプロパティに親関係も含む特定のオブジェクトがあります。このanalysisObjectRepositoryをクエリして、いくつかのオブジェクトを返します。
詳細
以下のコードは、ID値を含む特定のオブジェクトのルート、最初の子(直下の子)、および孫を返すことを想定しています。
以下のコードでは、3つの個別のOR条件のいずれかを真にするすべての結果が結果のように返されるべきであると常識は言っています。
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId ||
x.Parent.ID == packageId ||
x.Parent.Parent.ID == packageId)
.ToList();
ただし、上記のコードは、子と孫のみを返し、ルートオブジェクト(null Parent値)を返さず、
x.ID == packageId
条件が真。
2番目を作るオブジェクトのみ
x.Parent.ID == packageId
そして第三
x.Parent.Parent.ID == packageId
句が返されます。
以下のコードでルートオブジェクトを返すコードのみを記述した場合、それが返されるため、analysisObjectRepositoryにすべてのオブジェクトが含まれていることを完全に確認できます。
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(x => x.ID == packageId )
.ToList();
ただし、デリゲートとして書き換えると、予想される結果が得られ、予想されるすべてのオブジェクトが返されます。
List<AnalysisObject> analysisObjects =
analysisObjectRepository
.FindAll()
.Where(delegate(AnalysisObject x)
{
return
(x.ID == packageId) ||
(x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null &&
x.Parent.Parent != null &&
x.Parent.Parent.ID == packageId); })
.ToList();
質問
ラムダ式に何か不足していますか?それは本当に単純な3つの部分OR条件であり、3つの条件のいずれかを真にするすべてのオブジェクトが返されるべきだと思います。問題が、正確にそれを理解できませんでした。
どんな助けも素晴らしいでしょう。
2番目のデリゲートは、(ラムダではなく)匿名デリゲート形式の最初のデリゲートではありません。あなたの状態を見てください。
最初:
x.ID == packageId || x.Parent.ID == packageId || x.Parent.Parent.ID == packageId
第二:
(x.ID == packageId) || (x.Parent != null && x.Parent.ID == packageId) ||
(x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
ラムダの呼び出しは、IDが一致せず、親がnullまたは一致せず、祖父母がnullであるx
に対して例外をスローします。 nullチェックをラムダにコピーすると、正しく機能するはずです。
元のオブジェクトがList<T>
でない場合、FindAll()
の戻り値の種類と、これがIQueryable
インターフェイスを実装しているかどうかを知る方法がありません。もしそうなら、それはおそらく矛盾を説明しています。ラムダはコンパイル時にExpression<Func<T>>
ただし匿名デリゲートはできないに変換できるため、ラムダバージョンを使用する場合はIQueryable
の実装を使用し、使用する場合はLINQ-to-Objectsを使用する場合があります匿名委任バージョン。
これは、ラムダがNullReferenceException
を引き起こさない理由も説明します。そのラムダ式をIEnumerable<T>
がnotIQueryable<T>
を実装する何かに渡す場合、ラムダの実行時評価(他のメソッドと違いはありません、匿名かどうか) NullReferenceException
がターゲットと等しくなく、親または祖父母がnullであるオブジェクトに最初に遭遇したときに、ID
をスローします。
次の簡単な例を考えてみましょう。
IQueryable<MyObject> source = ...; // some object that implements IQueryable<MyObject>
var anonymousMethod = source.Where(delegate(MyObject o) { return o.Name == "Adam"; });
var expressionLambda = source.Where(o => o.Name == "Adam");
これら2つの方法では、まったく異なる結果が生成されます。
最初のクエリは単純なバージョンです。匿名メソッドの結果、デリゲートがIEnumerable<MyObject>.Where
拡張メソッドに渡され、source
の内容全体がデリゲートに対して(通常のコンパイル済みコードを使用してメモリ内で)チェックされます。つまり、C#のイテレータブロックに精通している場合、次のようになります。
public IEnumerable<MyObject> MyWhere(IEnumerable<MyObject> dataSource, Func<MyObject, bool> predicate)
{
foreach(MyObject item in dataSource)
{
if(predicate(item)) yield return item;
}
}
ここでの顕著な点は、実際にフィルタリングを実行していることですメモリ内クライアント側で。たとえば、ソースがSQL ORMである場合、クエリにはWHERE
句はありません。結果セット全体がクライアントに戻され、フィルタリングされますthere。
ラムダ式を使用する2番目のクエリはExpression<Func<MyObject, bool>>
に変換され、IQueryable<MyObject>.Where()
拡張メソッドを使用します。これにより、IQueryable<MyObject>
とも入力されるオブジェクトが作成されます。このすべては、expressionを基礎となるプロバイダーに渡すことで機能します。 これがNullReferenceException
を取得していない理由です。式(オブジェクトを使用する式のlogicの表現である、単に呼び出すことができる実際のコンパイルされた関数ではなく)をどのように変換するかは、クエリプロバイダー次第です。使える。
区別を確認する簡単な方法(または、少なくともis)を区別するには、ラムダバージョンでWhere
を呼び出す前にAsEnumerable()
を呼び出すことです。これにより、コードでLINQ-to-Objectsが使用されるようになり(ラムダバージョンのようにIEnumerable<T>
ではなく、匿名デリゲートバージョンのようにIQueryable<T>
で動作します)、次のように例外が取得されます期待される。
長所と短所は、ラムダ式がデータソースに対する何らかのクエリに変換されているのに対し、匿名メソッドバージョンはメモリ内のentireデータソースを評価していることです。ラムダをクエリに変換することは何でも、あなたが期待しているロジックを表していないため、期待する結果が得られません。
デリゲートと同じ条件でラムダを書いてみてください。このような:
List<AnalysisObject> analysisObjects =
analysisObjectRepository.FindAll().Where(
(x =>
(x.ID == packageId)
|| (x.Parent != null && x.Parent.ID == packageId)
|| (x.Parent != null && x.Parent.Parent != null && x.Parent.Parent.ID == packageId)
).ToList();
デリゲートでParent
プロパティのnullをチェックしています。同じことがラムダ式でも機能するはずです。
List<AnalysisObject> analysisObjects = analysisObjectRepository
.FindAll()
.Where(x =>
(x.ID == packageId) ||
(x.Parent != null &&
(x.Parent.ID == packageId ||
(x.Parent.Parent != null && x.Parent.Parent.ID == packageId)))
.ToList();