5

私はいくつかのコードを読みやすくしようとしていますが、拡張メソッドやその式をどのように構造化するかについては十分に理解していません。現在、私はできるよここでの目標は.ActiveRecords()LINQ to Entitiesは(関連エンティティの)メソッドを認識しません

のような拡張メソッドで.Where(RecordStatusTypeId != (int)RecordStatusTypes.Deleted)のような文を置き換えることです(IRecordStatusのインターフェイスから実装されている)それらのRecordStatusTypeId

public interface IRecordStatus 
{ 
    int RecordStatusTypeId { get; set; } 
    RecordStatusType RecordStatusType { get; set; } 
} 

を持っている多くの企業を持っています私はDbSet<T>IQueryable<T>、は、この拡張メソッドを持っている*

public static IQueryable<T> ActiveRecords<T>(this DbSet<T> entitySet) 
    where T : class, IRecordStatus 
{ 
    return entitySet.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted); 
} 

次の拡張メソッドでこれを達成するために、およびIEnumerable<T>

私のようなもの置き換えるしようとすると、これはMyDbContext.Entities.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deleted)ようなステートメントのための素晴らしい作品が、私はエラー「エンティティへのLINQのメソッドを認識しません」を取得:私は「何で

MyDbContext.Entities.Where(e => e.RelatedEntity.Where(re => re.RecordStatusTypeId != (int)RecordStatusTypes.Deleted)); 

をやりたいdは:私は、LINQの句内DbSet上だけでなく、関連するエンティティ上でアクティブなレコードをフィルタリングすることができるように

MyDbContext.Entities.Where(e => e.RelatedEntity.ActiveRecords().Any()); 

は、どのように私は私の拡張メソッドを変更(または式を追加する)ことができますか?

+0

エンティティフレームワークプラスはこれを行うことができます: https://github.com/zzzprojects/EntityFramework-Plus –

+0

@CyrilIselin、ありがとう、私はそれをチェックアウトします。 –

+1

メソッドをT-SQLに変換できませんでした。エンティティがそれを認識できませんでした。あなたの 'Entities'に' AsEnumerable'を追加して、もう一度やり直してください: 'MyDbContext.Entities.AsEnumerable()。Where ...'。 –

答えて

2

変換が間違っている可能性があります。 は、必要があります。

MyDbContext.Entities.Where(e => e.RelatedEntity.Where(re => re.RecordStatusTypeId != (int)RecordStatusTypes.Deleted)); 

はこれに変換します。

MyDbContext.Entities.Where(e => e.RelatedEntity.ActiveRecords().Any()); 
+0

はい、あなたは正しいです。残念なことに、それはポストを単純化しようとしている私の側のエラーでした。私はその問題を解決しました。 –

1

Queryable.WhereはSQLに変換することができます表現を期待し、ActiveRecordsはSQLに変換することができないので、あなたは例外を取得しています。

あなたがする必要があるのは、式を更新してActiveRecordsへの呼び出しを.Where(e => e.RecordStatusTypeId != (int)RecordStatusTypes.Deletedの呼び出しに展開することです。

私は、動作するソリューションの草稿を提供します。これは、あなたが提供した例に非常に特有です。あなたはおそらくそれを一般的にするためにそれに取り組むべきです。

次の式の訪問者は、基本的には、引数として適切な表現でWhereへの呼び出しにActiveRecordsへの呼び出しを変更します:

public class MyVisitor : ExpressionVisitor 
{ 
    protected override Expression VisitMethodCall(MethodCallExpression m) 
    { 
     if (m.Method.Name == "ActiveRecords") 
     { 
      var entityType = m.Method.GetGenericArguments()[0]; 
      var whereMethod = genericWhereMethod.MakeGenericMethod(entityType); 

      var param = Expression.Parameter(entityType); 
      var expressionToPassToWhere = 
       Expression.NotEqual(
        Expression.Property(param, "RecordStatusTypeId"), 
        Expression.Constant((int)RecordStatusTypes.Deleted)); 

      Expression newExpression = 
       Expression.Call(
        whereMethod, 
        m.Arguments[0], 
        Expression.Lambda(
         typeof(Func<,>).MakeGenericType(entityType, typeof(bool)), 
         expressionToPassToWhere, 
         param)); 

      return newExpression; 
     } 

     return base.VisitMethodCall(m); 
    } 

    //This is reference to the open version of `Enumerable.Where` 
    private static MethodInfo genericWhereMethod; 

    static MyVisitor() 
    { 
     genericWhereMethod = typeof (Enumerable).GetMethods(BindingFlags.Public | BindingFlags.Static) 
      .Where(x => x.Name == "Where" && x.GetGenericArguments().Length == 1) 
      .Select(x => new {Method = x, Parameters = x.GetParameters()}) 
      .Where(x => x.Parameters.Length == 2 && 
         x.Parameters[0].ParameterType.IsGenericType && 
         x.Parameters[0].ParameterType.GetGenericTypeDefinition() == typeof (IEnumerable<>) && 
         x.Parameters[1].ParameterType.IsGenericType && 
         x.Parameters[1].ParameterType.GetGenericTypeDefinition() == typeof (Func<,>)) 
      .Select(x => x.Method) 
      .Single(); 
    } 
} 

あなたはそれを渡す前に式を訪問するために特別なWhereSpecialメソッドを作成することができますQueryableの本当のWhere方法へ:

public static class ExtentionMethods 
{ 
    public static IQueryable<T> WhereSpecial<T>(this IQueryable<T> queryable, Expression<Func<T,bool>> expression) 
    { 
     MyVisitor visitor = new MyVisitor(); 

     var newBody = visitor.Visit(expression.Body); 

     expression = expression.Update(newBody, expression.Parameters); 

     return queryable.Where(expression); 
    } 
} 

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

var result = MyDbContext.Entities.WhereSpecial(e => e.RelatedEntity.ActiveRecords().Any()); 
+0

これはすべての最初の場所でヘルパーActiveRecordsメソッドを使用するの全目的を敗北させると思われる:) – Evk

+0

@エバーク、それはおそらく:)メソッド名と対応するラムダ式の辞書を取るより一般的な訪問者はそれになりますこのすべてをやる価値があります。 –

+0

@ YacoubMassad、私はこのような何かが努力する価値があるかどうか試してみる時間がなかったが、答えに感謝する。私が再訪する時間があれば、私は更新に戻るでしょう。 –

0

クエリの主要部分をIQueryableにして、where句にタグ付けしてみてください...その後、

IQueryable temp = from x in MyDbContext.Entities 
        select x; 

temp = temp.Where(e => e.RelatedEntity.ActiveRecords().Any()); 

または主要部分を評価し、メモリ内のどこを実行します。

ような何か。

+0

提案していただきありがとうございますが、このインターフェイスをさまざまなエンティティオブジェクトセットに適用していますので、少なくともActiveRecordsをフィルタリングしないでクエリの内容を取り戻すことはできません。 –

関連する問題