2016-07-15 13 views
0

エンティティフレームワーク

var query1 = dbContext.MainTable.Where(m => m.MainId == _mainId).SelectMany(sub => sub.SubTable1) 
    .Select(sub1 => new 
    { 
     sub1.CategoryName, 
     VisibleDivisions = sub1.SubTable2 
      .Where(sub2 => sub2.Status == "Visible") 
      .Select(sub2 => new 
      { 
       /* select only what needed */ 
      }) 
    }); 

私のメインテーブルから始めて、私はすべてSUB1のすべてと一緒に選択取得したいですサブ2はサブ1に関連しています。 クエリは期待通りに機能し、データベースにヒットする単一のクエリを生成します。

私の質問は内部Whereの部分についてです。このフィルタはアプリケーションのいくつかの他の部分で使用されます。だから、私はこの "目に見えるルール"を一箇所に定義したいと思っています(DRY原理)。私は、次のプロパティに

public static Expression<Func<SubTable2, bool>> VisibleOnlyExpression => sub2 => sub2.Status == "Visible"; 

を書かれており、これがInternal .NET Framework Data Provider error 1025.を述べ、私に例外をスロー

var query1 = dbContext.MainTable.Where(m => m.MainId == _mainId).SelectMany(sub => sub.SubTable1) 
    .Select(sub1 => new 
    { 
     sub1.CategoryName, 
     VisibleDivisions = sub1.SubTable2 
      .Where(VisibleOnlyExpression.Compile()) 
      .Select(sub2 => new 
      { 
       /* select only what needed */ 
      }) 
    }); 

に私のクエリを変更したFunc<SubTable2, bool>を期待しているところのよう

。 同じエラーで既に.Where(VisibleOnlyExpression.Compile())に変更しようとしました。

これは、EntityFrameworkがこれをSQLにトランスレートしようとしているためです。

私の質問は:どのようにして、コード内の単一の場所(DRY)に定義された "フィルタルール"をWhere - 、Select - 、...で使用できるようにすることができますかIQueryableと内部(サブ)クエリの場合はICollectionIsAwesome -ruleだけ素晴らしいメインのエントリを取得するために、後でサブ選択でICollection<MainTable>にフェッチするIQueryable<MainTable>に最初に呼び出されたのに対し、

var query = dbContext.MainTable 
    .Where(IsAwesome) 
    .SelectMany(s => s.SubTable1.Where(IsAlsoAwesome)) 
    .Select(sub => new 
    { 
     Sub1sub2s = sub.SubTable2.Where(IsVisible), 
     Sub2Mains = sub.MainTable.Where(IsAwesome) 
    }); 

私のような何かを書くことができるようにするのが大好きです特定のSubTable2エントリに関連する素晴らしいメインエントリのみ。しかし、MainTableエントリをすばらしいものとして定義するルールは、どこで呼び出すかにかかわらず同じです。

解決策には表現木の使用法とその操作方法が必要なので、単純なSQLに翻訳できるようになると思いますが、まずは正しいアイデアやポイントを得ることができません。

// You forgot to access ".Status" in your code. 
// Also you don't have to use "=>" to initialize "IsVisible". Use the regular "=". 
public static Expression<Func<SubTable2, bool>> IsVisible = sub2 => 
    sub2.Status == "Visible"; 

... 

VisibleDivisions = sub1 
    .SubTable2 
    // Don't call "Compile()" on your predicate expression. EF will do that. 
    .Where(IsVisibleOnly) 
    .Select(sub2 => new 
     { 
      /* select only what needed */ 
     }) 
+0

[LinqKit](http://www.albahari.com/nutshell/linqkit.aspx)のようなサードパーティライブラリが必要です。 –

+0

LinqKitで試してみました。同じ例外がスローされます。何か案は? – KingKerosin

答えて

0

public static IQueryable<SubTable2> VisibleOnly(this IQueryable<SubTable2> source) 
{ 
    return source.Where(s => s.Status == "Visible"); 
} 

アン、あなたはそのようにそれを使用することができます:

var query = dbContext.Table.VisibleOnly().Select(...) 
+0

これは 'Select >'の代わりに 'Where'が' Func 'を必要とするため、内部選択で失敗します。だから私は 'Compile()'を呼び出そうとしました。 – KingKerosin

0

私は以下のような拡張メソッドを作成します::私の知る限り、あなたがやろうとしているものは完全に可能でなければなりません

+0

しかし、これは生成されたSELECT文の 'Table'から全て(' * ')を選択しませんか?私は必要な値を受け取る(選択する)だけです – KingKerosin

+0

選択部分を省略しました。 'Select(a => a.Property)'など任意の値を選択するか、別の式を追加することができます。 'dbContext.Table.VisibleOnly1()。カスケードすることもできます。基底の型がSubTable2と一致する限り、(new = new.Property.VisibleOnly()')を選択します。 –

+0

内部部分 '{NewProp = a.Property。 'a.Property.AsQueryable()。VisibleOnly()'を呼び出すとコンパイルされますが、 'LINQ to Entities 'メソッドを認識できません' ActiveOnly() ) ' – KingKerosin

1

あなたは何を求めているのでしょうか。LinqKitAsExpandableInvoke拡張このような方法:まず、あなたがそうでなければ、例外が有名なEF「サポートされていないメソッド」を得るでしょう、変数に必要なすべての式をプルする必要があるため

var isAvesome = IsAwesome; 
var isAlsoAwesome = IsAlsoAwesome; 
var isVisible = IsVisible; 

var query = dbContext.MainTable 
    .AsExpandable() 
    .Where(mt => isAwesome.Invoke(mt)) 
    .SelectMany(s => s.SubTable1.Where(st1 => isAlsoAwesome.Invoke(st1))) 
    .Select(sub => new 
    { 
     Sub1sub2s = sub.SubTable2.Where(st2 => isVisible.Invoke(st2)), 
     Sub2Mains = sub.MainTable.Where(mt => isAwesome.Invoke(mt)) 
    }); 

は私が近いと言っています。そして、第二に、呼び出しはあなたの希望と同じくらい簡潔ではありません。しかし、少なくともそれはあなたがロジックを再利用できるようにします。

関連する問題