Skip to content

Instantly share code, notes, and snippets.

@xwipeoutx
Created June 6, 2016 13:01
Show Gist options
  • Save xwipeoutx/962b205324017c000c75899a8b5016d9 to your computer and use it in GitHub Desktop.
Save xwipeoutx/962b205324017c000c75899a8b5016d9 to your computer and use it in GitHub Desktop.
Simple, clean, fast IQueryable filter with expression chaining in C#
using System;
namespace ExpressionChainDemo
{
public class Car
{
public DateTime RegistrationDate { get; set; }
public DateTime PurchaseDate { get; set; }
}
}
using System;
using System.Linq;
namespace ExpressionChainDemo
{
public class CarFilter
{
public DateTime? RegistrationDateFrom { get; set; }
public DateTime? RegistrationDateTo { get; set; }
public DateTime? PurchaseDateFrom { get; set; }
public DateTime? PurchaseDateTo { get; set; }
public IQueryable<Car> ApplyTo(IQueryable<Car> carQuery)
{
return carQuery
.WhereDateBetween(car => car.RegistrationDate, RegistrationDateFrom, RegistrationDateTo)
.WhereDateBetween(car => car.PurchaseDate, PurchaseDateFrom, RegistrationDateTo);
}
}
}
using System;
using System.Linq;
using System.Linq.Expressions;
namespace ExpressionChainDemo
{
public static class Extensions
{
public static IQueryable<T> WhereDateBetween<T>(this IQueryable<T> source,
Expression<Func<T, DateTime>> getDate,
DateTime? fromDate, DateTime? toDate)
{
if (fromDate == null && toDate == null)
return source; // The simplest query is no query
var predicate = DateBetween(fromDate, toDate);
return source.Where(getDate.Chain(predicate));
}
private static Expression<Func<DateTime, bool>> DateBetween(DateTime? fromDate, DateTime? toDate)
{
if (toDate == null)
return date => fromDate <= date;
if (fromDate == null)
return date => toDate >= date;
return date => fromDate <= date && toDate >= date;
}
public static Expression<Func<TIn, TOut>> Chain<TIn, TInterstitial, TOut>(
this Expression<Func<TIn, TInterstitial>> inner,
Expression<Func<TInterstitial, TOut>> outer)
{
var visitor = new SwapVisitor(outer.Parameters[0], inner.Body);
return Expression.Lambda<Func<TIn, TOut>>(visitor.Visit(outer.Body), inner.Parameters);
}
}
}
using System.Linq.Expressions;
namespace ExpressionChainDemo
{
internal class SwapVisitor : ExpressionVisitor
{
private readonly Expression _source, _replacement;
public SwapVisitor(Expression source, Expression replacement)
{
_source = source;
_replacement = replacement;
}
public override Expression Visit(Expression node)
{
return node == _source ? _replacement : base.Visit(node);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment