Question

I'm writing a query framework, and trying to make it as generic as possible.

Let's say I have a query based on person, and I want the ability to filter on both the first and last names, and in both cases I want to be able to use filter conditions like StartsWith, 'EndsWith, Contains, Equals.

So now I have a method:

private Expression<Func<Person, bool>> FirstNameFilter(Comparator comparator, string compareValue) {
  switch (comparator) {
    case Comparator.Equal:
      return p => p.FirstName == compareValue;
    case Comparator.Contains:
      return p => p.FirstName.Contains(compareValue);
    case Comparator.StartsWith:
      return p => p.FirstName.StartsWith(compareValue);
    // etc.
  }
}

Now, I also want to be able to build the same filter for LastName. Seems silly and wasteful to copy and paste the whole thing over again, just replacing p.FirstName with p.LastName. I also have a bunch of other string fields that I want to filter on, and I really don't want to have to rewrite this whole method for each one!

Is there some way to abstract this, maybe using LinqKit, so that I can come out with a more generic method with the following approximate signature:

Expression<Func<Person, bool>> GetFilter(Expression<Func<Person, string>> stringExpression, Comparator comparator, string compareValue) {}

such that I could, inside FirstNameFilter, invoke it like so:

return GetFilter(p => p.FirstName, comparator, compareValue);
Était-ce utile?

La solution

Something like that (untested, but you have the idea) should help you to build the needed expressions :

public static class LinqQueries
{
    private static MethodInfo toLowerMethod = typeof(String).GetMethod("ToLower", Type.EmptyTypes);
    private static MethodInfo startsWithMethod= typeof(String).GetMethod("StartsWith", new Type[] { typeof(String) });
    private static MethodInfo containsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) });
    private static MethodInfo endsWithMethod= typeof(String).GetMethod("EndsWith", new Type[] { typeof(String) });

    public static Expression<Func<T, bool>> GetFilter(Expression<Func<T, string>> expression, Comparator comparator, string compareValue) {
        ParameterExpression parameterExpression = null;
        var memberExpression = GetMemberExpression(expression.Body, out parameterExpression);
        Expression constExp = Expression.Constant(compareValue);
        switch (comparator) {

        case Comparator.Contains:
          memberExpression = Expression.Call(memberExpression, containsMethod,constExp);
        break;
        case Comparator.StartsWith:
          memberExpression = Expression.Call(memberExpression, startsWithMethod, constExp);
        break;
        //etc.
        default :
          memberExpression = Expression.Equal(memberExpression, constExp);
        break;
      }

      return Expression.Lambda<Func<T, bool>>(memberExpression, new[]{parameterExpression});
  }


  private static Expression GetMemberExpression(Expression expression, out ParameterExpression parameterExpression)
    {
        parameterExpression = null;
        if (expression is MemberExpression)
        {
            var memberExpression = expression as MemberExpression;
            while (!(memberExpression.Expression is ParameterExpression))
                memberExpression = memberExpression.Expression as MemberExpression;
            parameterExpression = memberExpression.Expression as ParameterExpression;
            return expression as MemberExpression;
        }
        if (expression is MethodCallExpression)
        {
            var methodCallExpression = expression as MethodCallExpression;
            parameterExpression = methodCallExpression.Object as ParameterExpression;
            return methodCallExpression;
        }
        return null;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top