Skip to content

Instantly share code, notes, and snippets.

@WiredUK
Created October 9, 2015 23:53
Show Gist options
  • Save WiredUK/8f80e83dd742f6110f13 to your computer and use it in GitHub Desktop.
Save WiredUK/8f80e83dd742f6110f13 to your computer and use it in GitHub Desktop.
Logicizer
public static class Logicizer
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression)
{
var parameter = Expression.Parameter(typeof(T));
var left = new ReplaceExpressionVisitor(leftExpression.Parameters[0], parameter)
.Visit(leftExpression.Body);
var right = new ReplaceExpressionVisitor(rightExpression.Parameters[0], parameter)
.Visit(rightExpression.Body);
return Expression.Lambda<Func<T, bool>>(Expression.And(left, right), parameter);
}
public static Expression<Func<T, bool>> Or<T>(params Expression<Func<T, bool>>[] expressions)
{
if(expressions.Length < 2)
throw new ArgumentException("Must have at least 2 expressions");
if (expressions.Length > 2)
return Or<T>(expressions[0], Or<T>(expressions.Skip(1).ToArray()));
var parameter = Expression.Parameter(typeof(T));
var left = new ReplaceExpressionVisitor(expressions[0].Parameters[0], parameter)
.Visit(expressions[0].Body);
var right = new ReplaceExpressionVisitor(expressions[1].Parameters[0], parameter)
.Visit(expressions[1].Body);
return Expression.Lambda<Func<T, bool>>(Expression.Or(left, right), parameter);
}
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var parameter = Expression.Parameter(typeof(T));
var expressionVisitor = new ReplaceExpressionVisitor(expression.Parameters[0], parameter)
.Visit(expression.Body);
return Expression.Lambda<Func<T, bool>>(Expression.Not(expressionVisitor), parameter);
}
}
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
return node == _oldValue ? _newValue : base.Visit(node);
}
}
@WiredUK
Copy link
Author

WiredUK commented Oct 9, 2015

Some helper methods to provide And/Or/Not operation on generic expressions. Done like this means they are also compatible with Entity Framework. So for example you can do this:

var makes = new [] {"Toyota", "Ford"};

var cars = ctx.Cars.Where(GetMakeFilter(makes));

private Expression<Func<Car, bool>> GetMakeFilter(IEnumerable<string> makes)
{
    return Logicizer.Or<Car>(makes.Select(m =>
    {
        Expression<Func<Car, bool>> exp = c => c.Make == m;
        return exp;
    }).ToArray());
}

And Entity Framework will generate a SQL query something like this:

SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Wheels] AS [Wheels], 
    [Extent1].[Model] AS [Model], 
    [Extent1].[Make] AS [Make]
    FROM [dbo].[Cars] AS [Extent1]
    WHERE [Extent1].[Make] IN (@p__linq__0,@p__linq__1)}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment