2017-02-14 41 views
5

Mam następujący problem:Czy jest możliwe uzyskanie predykatu (Expression <Func<T,bool>>) używanego na IQueryable <T> i zastosowanie go do innej funkcji Lambda?

  • Dostaję IQueryable<T> z LinqToQueryString
  • Ten IQueryable<T> służy do kwerendy MongoDB
  • IQueryable<T> służy do powrotu paged zbiór danych, a także całkowitą liczbę elementów w celu ustalenia strony itp.
  • MongoDB dodaje grupę do liczby na IQueryable<T>.Where().Count(). Powoduje to, że operacje Count działają bardzo wolno.

Możliwe rozwiązanie:

  • Pobierz Expression<Func<T,bool>> z oryginalnego IQueryable<T> i zastosować go do mongoCollection<T>.Count(filter). Pomija to problem.

starałem się uzyskać „Gdzie” z IQueryable<T> .Expression a następnie manipulować ExpressionType do formatu, który może być używany w DynamicExpression.ParseLambda(). W większości przypadków działało to dobrze, dopóki nie przetestowałem kodu z wyrażeniem DateTime.

Dołączyłem skrypt LINQPad, który używa lokalnej instalacji MongoDB do zapełniania danych, a następnie zlicza za pomocą nowego wyrażenia utworzonego z ExpressionVisitor.

Mam nadzieję, że istnieje prostszy sposób ponownego użycia "Where" z oryginalnego wyrażenia w nowym MongoDB FilterDefinitionBuilder<T>.Where(originalWhereExpression).

zależności Script to: References (F4)

Kod:

<Query Kind="Program"> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.dll</Reference> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.Expressions.dll</Reference> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.Queryable.dll</Reference> 
    <NuGetReference>Faker</NuGetReference> 
    <NuGetReference>LINQKit.Core</NuGetReference> 
    <NuGetReference>mongocsharpdriver</NuGetReference> 
    <NuGetReference>MongoDB.Driver</NuGetReference> 
    <NuGetReference>NBuilder</NuGetReference> 
    <NuGetReference>Newtonsoft.Json</NuGetReference> 
    <NuGetReference>System.Linq.Dynamic</NuGetReference> 
    <Namespace>FizzWare.NBuilder</Namespace> 
    <Namespace>LinqKit</Namespace> 
    <Namespace>MongoDB.Bson</Namespace> 
    <Namespace>MongoDB.Bson.Serialization.Attributes</Namespace> 
    <Namespace>MongoDB.Driver</Namespace> 
    <Namespace>MongoDB.Driver.Builders</Namespace> 
    <Namespace>MongoDB.Driver.Linq</Namespace> 
    <Namespace>myAlias = System.Linq.Dynamic</Namespace> 
    <Namespace>Newtonsoft.Json</Namespace> 
    <Namespace>System.Linq</Namespace> 
    <Namespace>System.Linq.Expressions</Namespace> 
    <Namespace>System.Threading.Tasks</Namespace> 
    <Namespace>System.Threading.Tasks.Dataflow</Namespace> 
</Query> 

private string _mongoDBConnectionString = "mongodb://localhost"; 
private string _mongoDBDatabase = "LinqToQ"; 
private string _mongoDBCollection = "People"; 

private IMongoClient _mongoClient; 
private IMongoDatabase _mongoDb; 

private int _demoCount = 100000; 
private bool _doPrep = true; 

void Main() 
{ 
    _connectToMongoDB(); 

    if (_doPrep) 
     _prepMongo(); 

    var mongoDataQuery = _queryDemoData().Result; 
    mongoDataQuery.Expression.ToString().Dump("Original Expression"); 

    var whereFinder = new WhereFinder(); 
    whereFinder.SetWhere(mongoDataQuery.Expression); 

    var tempColl = _getPeopleCollection(); 

    if (!string.IsNullOrEmpty(whereFinder.WhereClause)) 
    { 
     var filter = new FilterDefinitionBuilder<Person>(); 
     tempColl.Count(filter.Where(_createWherePredicate<Person>(whereFinder.GetLambdaParts<Person>()))).Dump("Dynamic where count"); 
    } 
    else 
     tempColl.Count(FilterDefinition<Person>.Empty).Dump("No filter count"); 

    "Done".Dump(); 
} 

// Define other methods and classes here 

// 
private void _replaceExpressionTypes(ref StringBuilder whereBuilder, Dictionary<ExpressionType,string> expressionTypes) 
{ 
    foreach (var expType in expressionTypes.Keys) 
    { 
     whereBuilder.Replace($" {expType} ", $" {expressionTypes[expType]} "); 
    } 

    var openBracketCount = whereBuilder.ToString().Count(s => s == char.Parse("(")); 
    var closeBracketCount = whereBuilder.ToString().Count(s=> s==char.Parse(")")); 

    //whereBuilder.Replace("new DateTime(1974, 1, 1)","\"1974-01-01T00:00:00.00Z\""); 
    whereBuilder.Insert(0,"(",1); 
    whereBuilder.Append(")"); 

    $"OpenBrackets: {openBracketCount} vs CloseBrackets: {closeBracketCount}".Dump("Found Brackets"); 

    if(openBracketCount==closeBracketCount) 
     return; 
    if (openBracketCount > closeBracketCount) 
    { 
     var firstopenBracket = whereBuilder.ToString().IndexOf("("); 
     whereBuilder.Remove(firstopenBracket,1); 
    } 
    var lastCloseBracket = whereBuilder.ToString().LastIndexOf(")"); 
    if(lastCloseBracket>-1) 
     whereBuilder.Remove(lastCloseBracket,1); 
} 
private Dictionary<ExpressionType, string> _buildExpressionTypePairs() 
{ 
    var result = new Dictionary<ExpressionType, string>(); 

    result.Add(ExpressionType.Not, "!"); 
    result.Add(ExpressionType.Add, "+"); 
    result.Add(ExpressionType.AddChecked, "+"); 
    result.Add(ExpressionType.Subtract, "-"); 
    result.Add(ExpressionType.SubtractChecked, "-"); 
    result.Add(ExpressionType.Multiply, "*"); 
    result.Add(ExpressionType.MultiplyChecked, "*"); 
    result.Add(ExpressionType.Divide, "/"); 
    result.Add(ExpressionType.Modulo, "%"); 
    result.Add(ExpressionType.And, "&"); 
    result.Add(ExpressionType.AndAlso, "&&"); 
    result.Add(ExpressionType.Or, "|"); 
    result.Add(ExpressionType.OrElse, "||"); 
    result.Add(ExpressionType.LessThan, "<"); 
    result.Add(ExpressionType.LessThanOrEqual, "<="); 
    result.Add(ExpressionType.GreaterThan, ">"); 
    result.Add(ExpressionType.GreaterThanOrEqual, ">="); 
    result.Add(ExpressionType.Equal, "=="); 
    result.Add(ExpressionType.NotEqual, "!="); 

    return result; 
} 
private Expression<Func<Person, bool>> _createWherePredicate<T>(LamdaParts<T> lamdaParts) 
{ 
    var whereBuilder = new StringBuilder(lamdaParts.ExpressionString); 
    _replaceExpressionTypes(ref whereBuilder, _buildExpressionTypePairs()); 

    whereBuilder.ToString().Dump("Manipulated where cluase"); 

    var parameter = Expression.Parameter(lamdaParts.ParamterType, lamdaParts.ExpressionParameter); 
    //lamdaParts.ParamterType.Dump("Parameter"); 
    //var parameter = Expression.Parameter(typeof(Person), "p"); 
    var expression = myAlias.DynamicExpression.ParseLambda(new[] { parameter }, null, whereBuilder.ToString()); 

    //return Expression.Lambda<Func<Person, bool>>(whereExpression, parameter); 
    return Expression.Lambda<Func<Person, bool>>(expression.Body, expression.Parameters); 
} 
private async Task<IMongoQueryable<Person>> _queryDemoData() 
{ 
    var people = _getPeopleCollection(); 

    return people.AsQueryable().Where(p => p.DateOfBirth <= new DateTime(1974, 1, 1)); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f") && p.DateOfBirth >= new DateTime(1968, 1, 1) && p.DateOfBirth < new DateTime(1974, 1, 1)); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f") && (p.DateOfBirth>=new DateTime(1968,1,1) && p.DateOfBirth<new DateTime(1974,1,1))); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f")); 
    //return people.AsQueryable().Where(p => p.FirstName.Contains("f")); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson"); 

} 
private void _prepMongo() 
{ 
    _mongoDb.DropCollection(_mongoDBCollection, CancellationToken.None); 

    var testData = _getDemoList(_demoCount); 
    var people = _getPeopleCollection(); 

    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.LastName)); 
    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.Email)); 

    testData.ForEachOverTpl((person) => 
    { 
     people.InsertOneAsync(person).Wait(); 
    }); 

    $"Inserted {testData.Count} demo records".Dump(); 
} 
private IList<Person> _getDemoList(int demoCount) 
{ 
    var result = Builder<Person>.CreateListOfSize(demoCount) 
     .All() 
     .With(p => p.FirstName = Faker.NameFaker.FirstName()) 
     .With(p => p.LastName = Faker.NameFaker.LastName()) 
     .With(p => p.Email = Faker.InternetFaker.Email()) 
     .With(p => p.DateOfBirth = Faker.DateTimeFaker.BirthDay(21,50)) 
     .Build(); 

    return result; 
} 
private IMongoCollection<Person> _getPeopleCollection() 
{ 
    return _mongoDb.GetCollection<Person>(_mongoDBCollection); 
} 
private void _connectToMongoDB() 
{ 
    _mongoClient = new MongoClient(_mongoDBConnectionString); 
    _mongoDb = _mongoClient.GetDatabase(_mongoDBDatabase); 
} 

public class Person 
{ 
    [BsonId] 
    public string Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Email { get; set; } 
    public DateTime DateOfBirth { get; set; } 

} 
public class WhereFinder : MongoDB.Driver.Linq.ExpressionVisitor 
{ 
    //private IList<MethodCallExpression> whereExpressions = new List<MethodCallExpression>(); 
    private bool _foundWhere = false; 
    private bool _setWhere = false; 

    public string WhereClause { get; set; } 
    public string Parameter { get; set; } 


    public LamdaParts<T> GetLambdaParts<T>() 
    { 
     return new LamdaParts<T> { 
      ExpressionParameter=Parameter, 
      ExpressionString = WhereClause 
     }; 
    } 
    public void SetWhere(Expression expression) 
    { 
     Visit(expression); 
     //return whereExpressions; 
    } 

    protected override Expression VisitBinary(BinaryExpression node) 
    { 
     //$"{node.Left} {_convertNodeType(node.NodeType)} {node.Right}".Dump(); 
     if (_foundWhere && !_setWhere) 
     { 
      //node.ToString().Dump("VisitBinary"); 
      $"{node.Left} {_convertNodeType(node.NodeType)} {node.Right}".Dump("Setting Where Clause"); 
      WhereClause= $"{node.Left} {_convertNodeType(node.NodeType)} {node.Right}"; 
      //WhereClause.Dump("WhereClause"); 
      _setWhere=true; 
     } 
     return base.VisitBinary(node); 
    } 
    private string _convertNodeType(ExpressionType nodeType) 
    { 
     switch (nodeType) 
     { 
      case ExpressionType.Not: 
       return "!"; 
      case ExpressionType.Add: 
      case ExpressionType.AddChecked: 
       return "+"; 
      case ExpressionType.Subtract: 
      case ExpressionType.SubtractChecked: 
       return "-"; 
      case ExpressionType.Multiply: 
      case ExpressionType.MultiplyChecked: 
       return "*"; 
      case ExpressionType.Divide: 
       return "/"; 
      case ExpressionType.Modulo: 
       return "%"; 
      case ExpressionType.And: 
       return "&"; 
      case ExpressionType.AndAlso: 
       return "&&"; 
      case ExpressionType.Or: 
       return "|"; 
      case ExpressionType.OrElse: 
       return "||"; 
      case ExpressionType.LessThan: 
       return "<"; 
      case ExpressionType.LessThanOrEqual: 
       return "<="; 
      case ExpressionType.GreaterThan: 
       return ">"; 
      case ExpressionType.GreaterThanOrEqual: 
       return ">="; 
      case ExpressionType.Equal: 
       return "=="; 
      case ExpressionType.NotEqual: 
       return "!="; 
      default: 
       throw new Exception(string.Format("Unhandled expression type: '{0}'", nodeType)); 
     } 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (_foundWhere) 
     { 
      //node.ToString().Dump("VisitParameter"); 
      Parameter=node.ToString(); 
     } 
     return base.VisitParameter(node); 
    } 

    protected override Expression VisitMethodCall(MethodCallExpression expression) 
    { 
     if (expression.Method.Name == "Where") 
     { 
      //whereExpressions.Add(expression); 
      _foundWhere = true; 
     } 

     if (expression?.Arguments != null) 
     { 
      foreach (var arg in expression.Arguments) 
      { 
       Visit(arg); 
      } 
     } 

     return expression; 
    } 
} 
public class LamdaParts<T> 
{ 
    public Type ParamterType 
    { 
     get 
     { 
      return typeof(T); 
     } 
    } 
    public string ExpressionParameter { get; set; } 
    public string ExpressionString { get;set;} 
} 
public static class Extensions 
{ 
    public static void ForEachOverTpl<T>(this IEnumerable<T> enumerable, Action<T> call) 
    { 
     var cancellationTokenSource = new CancellationTokenSource(); 
     var actionBlock = new ActionBlock<T>(call, new ExecutionDataflowBlockOptions 
     { 
      TaskScheduler = TaskScheduler.Current, 
      MaxDegreeOfParallelism = Environment.ProcessorCount * 2, 
      CancellationToken = cancellationTokenSource.Token, 
     }); 
     foreach (T item in enumerable) 
     { 
      if (cancellationTokenSource.IsCancellationRequested) return; 
      actionBlock.Post(item); 
     } 
     actionBlock.Complete(); 
     actionBlock.Completion.Wait(cancellationTokenSource.Token); 
    } 
} 

Odpowiedz

3

Rozwiązanie jest dość proste, po zrozumieniu drzewa wyrażenie i ewentualne nazwy metod ekspresji poziomie głównym. Dzięki @bolanki za pomoc.

załączeniu zaktualizowany skrypt LINQPad (do testu ustawić _doPrep = true):

<Query Kind="Program"> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.dll</Reference> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.Expressions.dll</Reference> 
    <Reference>&lt;RuntimeDirectory&gt;\System.Linq.Queryable.dll</Reference> 
    <NuGetReference>Faker</NuGetReference> 
    <NuGetReference>mongocsharpdriver</NuGetReference> 
    <NuGetReference>MongoDB.Driver</NuGetReference> 
    <NuGetReference>NBuilder</NuGetReference> 
    <NuGetReference>Newtonsoft.Json</NuGetReference> 
    <NuGetReference>System.Linq.Dynamic</NuGetReference> 
    <Namespace>FizzWare.NBuilder</Namespace> 
    <Namespace>MongoDB.Bson</Namespace> 
    <Namespace>MongoDB.Bson.Serialization.Attributes</Namespace> 
    <Namespace>MongoDB.Driver</Namespace> 
    <Namespace>MongoDB.Driver.Builders</Namespace> 
    <Namespace>MongoDB.Driver.Linq</Namespace> 
    <Namespace>myAlias = System.Linq.Dynamic</Namespace> 
    <Namespace>Newtonsoft.Json</Namespace> 
    <Namespace>System.Linq</Namespace> 
    <Namespace>System.Linq.Expressions</Namespace> 
    <Namespace>System.Threading.Tasks</Namespace> 
    <Namespace>System.Threading.Tasks.Dataflow</Namespace> 
</Query> 

private string _mongoDBConnectionString = "mongodb://localhost"; 
private string _mongoDBDatabase = "LinqToQ"; 
private string _mongoDBCollection = "People"; 

private IMongoClient _mongoClient; 
private IMongoDatabase _mongoDb; 

private int _demoCount = 2000000; 
private bool _doPrep = false; 

void Main() 
{ 
    _connectToMongoDB(); 

    // Should demo data be generated 
    if (_doPrep) 
     _prepMongo(); 

    // Get the queryable to test with 
    var mongoDataQuery = _getIQueryable(); 
    // Print the original expression  
    //mongoDataQuery.Expression.ToString().Dump("Original Expression"); 

    // Evaluate the expression and try find the where expression 
    var whereFinder = new WhereFinder<Person>(mongoDataQuery.Expression); 

    // Get the MongoCollection to be Filtered and Count 
    var tempColl = _getPeopleCollection(); 

    if (whereFinder.FoundWhere) 
    { 
     //whereFinder.TheWhereExpression.ToString().Dump("Calculated where expression"); 
     var filter = new FilterDefinitionBuilder<Person>(); 
     var stopwatch = new Stopwatch(); 
     stopwatch.Start(); 
     tempColl.Count(filter.Where(whereFinder.TheWhereExpression)).Dump("Dynamic where count"); 
     var afterCalculatedWhere = stopwatch.Elapsed; 
     mongoDataQuery.Count().Dump("IQueryable<T> where count"); 
     var afterIQuerableWhere = stopwatch.Elapsed; 
     stopwatch.Stop(); 

     $"Calculated where:{afterCalculatedWhere:c}\nIQueryable where:{afterIQuerableWhere:c}".Dump("Where Durations"); 
    } 
    else 
     tempColl.Count(FilterDefinition<Person>.Empty).Dump("No filter count"); 

    "Done".Dump(); 
} 

/////////////////////////////////////////////////////// 
// END SOLUTION 
/////////////////////////////////////////////////////// 
private IMongoQueryable<Person> _getIQueryable() 
{ 
    var people = _getPeopleCollection(); 

    //return people.AsQueryable().Where(p => p.DateOfBirth <= new DateTime(1974, 1, 1)); 
    return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f") && p.DateOfBirth >= new DateTime(1968, 1, 1) && p.DateOfBirth < new DateTime(1974, 1, 1)); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f") && (p.DateOfBirth>=new DateTime(1968,1,1) && p.DateOfBirth<new DateTime(1974,1,1))); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson" && p.FirstName.Contains("f")); 
    //return people.AsQueryable().Where(p => p.FirstName.Contains("f")); 
    //return people.AsQueryable().Where(p => p.LastName == "Anderson"); 

} 
public class Person 
{ 
    [BsonId] 
    public string Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string Email { get; set; } 
    public DateTime DateOfBirth { get; set; } 

} 
public class WhereFinder<T> : MongoDB.Driver.Linq.ExpressionVisitor 
{ 
    private bool _processingWhere = false; 
    private bool _processingLambda = false; 
    public ParameterExpression _parameterExpression { get; set; } 

    public WhereFinder(Expression expression) 
    { 
     Visit(expression); 
    } 

    public Expression<Func<T, bool>> TheWhereExpression { get; set; } 
    public bool FoundWhere 
    { 
     get { return TheWhereExpression != null; } 
    } 

    protected override Expression VisitBinary(BinaryExpression node) 
    { 
     var result = base.VisitBinary(node); 
     if(_processingWhere) 
      TheWhereExpression = (Expression<Func<T, bool>>)Expression.Lambda(node, _parameterExpression); 
     return result; 
    } 
    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (_processingWhere || _processingLambda || _parameterExpression==null) 
      _parameterExpression = node; 
     return base.VisitParameter(node); 
    } 
    protected override Expression VisitMethodCall(MethodCallExpression expression) 
    { 
     string methodName = expression.Method.Name; 
     if (TheWhereExpression==null && (methodName == "Where" || methodName == "Contains")) 
     { 
      _processingWhere = true; 
      if (expression?.Arguments != null) 
       foreach (var arg in expression.Arguments) 
        Visit(arg); 
      _processingWhere = false; 
     } 

     return expression; 
    } 
    protected override Expression VisitLambda(LambdaExpression exp) 
    { 
     if (_parameterExpression == null) 
      _parameterExpression = exp.Parameters?.FirstOrDefault(); 

     TheWhereExpression = (Expression<Func<T, bool>>)Expression.Lambda(exp.Body, _parameterExpression); 
     return exp; 
    } 

} 
/////////////////////////////////////////////////////// 
// END SOLUTION 
/////////////////////////////////////////////////////// 



/////////////////////////////////////////////////////// 
// BEGIN DEMO DATA 
/////////////////////////////////////////////////////// 
private void _prepMongo() 
{ 
    _mongoDb.DropCollection(_mongoDBCollection, CancellationToken.None); 

    var testData = _getDemoList(_demoCount); 
    var people = _getPeopleCollection(); 

    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.FirstName)); 
    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.LastName)); 
    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.Email)); 
    people.Indexes.CreateOne(Builders<Person>.IndexKeys.Ascending(_ => _.DateOfBirth)); 

    $"Inserting ...{testData.Count}... demo records".Dump(); 

    Extensions.ForEachOverTpl<Person>(testData, (person) => 
    { 
     people.InsertOneAsync(person).Wait(); 
    }); 

    $"Inserted {testData.Count} demo records".Dump(); 
} 
private IList<Person> _getDemoList(int demoCount) 
{ 
    var result = Builder<Person>.CreateListOfSize(demoCount) 
     .All() 
     .With(p => p.FirstName = Faker.NameFaker.FirstName()) 
     .With(p => p.LastName = Faker.NameFaker.LastName()) 
     .With(p => p.Email = Faker.InternetFaker.Email()) 
     .With(p => p.DateOfBirth = Faker.DateTimeFaker.BirthDay(21, 50)) 
     .Build(); 

    return result; 
} 
private IMongoCollection<Person> _getPeopleCollection() 
{ 
    return _mongoDb.GetCollection<Person>(_mongoDBCollection); 
} 
private void _connectToMongoDB() 
{ 
    _mongoClient = new MongoClient(_mongoDBConnectionString); 
    _mongoDb = _mongoClient.GetDatabase(_mongoDBDatabase); 
} 
/////////////////////////////////////////////////////// 
// END DEMO DATA 
/////////////////////////////////////////////////////// 
public static class Extensions 
{ 
    public static void ForEachOverTpl<T>(this IEnumerable<T> enumerable, Action<T> call) 
    { 
     var cancellationTokenSource = new CancellationTokenSource(); 
     var actionBlock = new ActionBlock<T>(call, new ExecutionDataflowBlockOptions 
     { 
      TaskScheduler = TaskScheduler.Current, 
      MaxDegreeOfParallelism = Environment.ProcessorCount * 2, 
      CancellationToken = cancellationTokenSource.Token, 
     }); 
     foreach (T item in enumerable) 
     { 
      if (cancellationTokenSource.IsCancellationRequested) return; 
      actionBlock.Post(item); 
     } 
     actionBlock.Complete(); 
     actionBlock.Completion.Wait(cancellationTokenSource.Token); 
    } 
} 

Aktualizacja: - Fix dla wyrażeń zawierających Weźmy OrderBy itp

public class WhereFinder<T> : MongoDB.Driver.Linq.ExpressionVisitor 
    { 
     private bool _processingWhere = false; 
     private bool _processingLambda = false; 
     public ParameterExpression _parameterExpression { get; set; } 

     public WhereFinder(Expression expression) 
     { 
     Visit(expression); 
     } 

     public Expression<Func<T, bool>> TheWhereExpression { get; set; } 
     public bool FoundWhere 
     { 
     get { return TheWhereExpression != null; } 
     } 

     protected override Expression Visit(Expression exp) 
     { 
     return base.Visit(exp); 
     } 
     protected override Expression VisitBinary(BinaryExpression node) 
     { 
     var result = base.VisitBinary(node); 
     if (_processingWhere) 
     { 
      TheWhereExpression = (Expression<Func<T, bool>>) Expression.Lambda(node, _parameterExpression); 
     } 
     return result; 
     } 
     protected override Expression VisitParameter(ParameterExpression node) 
     { 
     if (_processingWhere || _processingLambda || _parameterExpression == null) 
      _parameterExpression = node; 
     return base.VisitParameter(node); 
     } 
     protected override Expression VisitMethodCall(MethodCallExpression expression) 
     { 
     string methodName = expression.Method.Name; 

     if (methodName == "Where") 
      _processingWhere = true; 
     if (expression?.Arguments != null) 
      foreach (var arg in expression.Arguments) 
      Visit(arg); 
     _processingWhere = false; 

     return expression; 
    } 
    protected override Expression VisitLambda(LambdaExpression exp) 
    { 
     if (_processingWhere) 
     { 
      if (_parameterExpression == null) 
       _parameterExpression = exp.Parameters?.FirstOrDefault(); 

      TheWhereExpression = (Expression<Func<T, bool>>)Expression.Lambda(exp.Body, _parameterExpression); 
     } 
     return exp; 
    } 

} 
Powiązane problemy