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:
- Is there anything that I should be checking, which I am not already checking?
- Is returning a truth expression a sensible default?
Any refactorings/improvements are welcome.
1 Answer 1
I guess that you're assuming that the MethodCall expression should be a call to Queryable.Where(...) ?
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;
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; }
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.