More Dynamic Queries

Took this slightly further (which you could definitely describe as making it worse :-)) by adding a limited form of an OrElse;

namespace MyExtensions
{
  public enum Operand
  {
    Equal,
    NotEqual,
    LessThan,
    LessThanEqual,
    GreaterThan,
    GreaterThanEqual
  }
  public static class Extensions
  {
    public static IQueryable<T> AddOrElse<T, V, U>(this IQueryable<T> queryable,
      string lhsName, Operand lhsOperand, V lhsValue,
      string rhsName, Operand rhsOperand, U rhsValue)
    {
      ParameterExpression pe = Expression.Parameter(typeof(T), "p");

      IQueryable<T> query = queryable.Where<T>(
        Expression.Lambda<Func<T, bool>>(Expression.OrElse(
          MakeBinaryExpression<T, V>(pe, lhsName, lhsOperand, lhsValue),
          MakeBinaryExpression<T, U>(pe, rhsName, rhsOperand, rhsValue)),
          new ParameterExpression[] { pe }));

      return (query);
    }
    public static IQueryable<T> AddClause<T, V>(this IQueryable<T> queryable,
      string propertyName, Operand operand, V propertyValue)
    {
      ParameterExpression pe = Expression.Parameter(typeof(T), "p");

      IQueryable<T> query = queryable.Where<T>(
        Expression.Lambda<Func<T, bool>>(
          MakeBinaryExpression<T, V>(pe, propertyName, operand, propertyValue),
          new ParameterExpression[] { pe }));

      return (query);
    }
    private static BinaryExpression MakeBinaryExpression<T, V>(
      ParameterExpression parameter, string propertyName, Operand operand, V propertyValue)
    {
      Func<Expression, Expression, bool, MethodInfo, BinaryExpression>
        fn = GetFuncForOperand(operand);

      BinaryExpression expression =
          fn(Expression.Property(
            parameter,
            typeof(T).GetProperty(propertyName)),
            Expression.Constant(propertyValue, typeof(V)),
            false,
            null);

      return (expression);
    }
    private static Func<Expression, Expression, bool, MethodInfo, BinaryExpression> GetFuncForOperand(Operand operand)
    {
      Func<Expression, Expression, bool, MethodInfo, BinaryExpression> func = null;

      switch (operand)
      {
        case Operand.Equal:
          func = Expression.Equal;
          break;
        case Operand.NotEqual:
          func = Expression.NotEqual;
          break;
        case Operand.LessThan:
          func = Expression.LessThan;
          break;
        case Operand.LessThanEqual:
          func = Expression.LessThanOrEqual;
          break;
        case Operand.GreaterThan:
          func = Expression.GreaterThan;
          break;
        case Operand.GreaterThanEqual:
          func = Expression.GreaterThanOrEqual;
          break;
        default:
          break;
      }
      return (func);
    }
  }
}

 

and then that can be used as in;

 static void Main(string[] args)
    {
      NorthwindDataContext ctx = new NorthwindDataContext("server=.;database=northwind");

      var query = from p in ctx.Products
                  select p;

      int prod = 22;
      short? units = 120;

      query = query.AddOrElse(
        "ProductID", Operand.Equal, prod,
        "UnitsInStock", Operand.GreaterThan, units);

      foreach (Product v in query)
      {
        Console.WriteLine(v.ProductID);
      }
    }

 

I’m going to stop doing this now – it’s making my head hurt 🙂