4
\$\begingroup\$

I am following up on this answer for a scenario that I am currently working on. Like the OP, I am too concerned about the longevity of the code.

public IQueryable<TEntity> EmptyEnumerable()
{
 return Enumerable.Empty<TEntity>().AsQueryable();
}
private Expression<Func<TEntity, bool>> GetFilterExpression(FilterQueryOption filter)
{
 var enumerable = this.EmptyEnumerable();
 var param = Expression.Parameter(typeof(TEntity));
 if(filter != null)
 {
 enumerable = (IQueryable<TEntity>)filter.ApplyTo(enumerable, new ODataQuerySettings());
 var mce = enumerable.Expression as MethodCallExpression;
 if(mce != null)
 {
 var quote = mce.Arguments[1] as UnaryExpression;
 if(quote != null)
 {
 return quote.Operand as Expression<Func<TEntity, bool>>;
 }
 }
 }
 return Expression.Lambda<Func<TEntity, bool>>(Expression.Constant(true), param);
}

My questions are:

  1. Is there anything that I should be checking, which I am not already checking?
  2. Is returning a truth expression a sensible default?

Any refactorings/improvements are welcome.

asked Jun 24, 2014 at 10:12
\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

I guess that you're assuming that the MethodCall expression should be a call to Queryable.Where(...) ?

  1. You should check that explicitly via something like :

    // yea, that's ugly. But that's for the demo. 
    // I'm pretty sure you'll find the proper way to get the method info with your favorite method
    static readonly MethodInfo QueryableWhere = ((MethodCallExpression)(
     (Expression<Func<IQueryable<string>>>)
     (() => ((IQueryable<string>)null).Where(x => true)))
     .Body.Unquote())
     .Method.GetGenericMethodDefinition();
    

    and in your code

     if(!mce.Method.IsGeneric || mce.Method.GetGenericMethodDefinition() != QueryableWhere)
     throw an exception;
    
  2. Unary expressions are not necessarily quotes! (a negation is an unary, for instance). Thus, you want to check mce.Arguments[1].NodeType instead of checking its type.

    Moreover, quote un-quoting is quite common, so it can be useful to replace.

    var quote = mce.Arguments[1] as UnaryExpression;
    if(quote != null)
    {
     return quote.Operand as Expression<Func<TEntity, bool>>;
    }
    

    by

    return mce.Arguments[1].Unquote();
    

    with

    public static Expression Unquote(this Expression quote)
    {
     if(quote.NodeType == ExpressionType.Quote)
     return ((UnaryExpression)quote).Operand.Unquote();
     return quote;
    }
    
  3. Returning a lambda is OK in my opinion. No problem with that IF AND ONLY IF there is no chance that the enclosed predicate does not contains any reference to an outer parameter (or variable), which is the case here, so it's okay.

Jamal
35.2k13 gold badges134 silver badges238 bronze badges
answered Jun 24, 2014 at 14:36
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.