Created
June 6, 2016 13:01
-
-
Save xwipeoutx/962b205324017c000c75899a8b5016d9 to your computer and use it in GitHub Desktop.
Simple, clean, fast IQueryable filter with expression chaining in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace ExpressionChainDemo | |
{ | |
public class Car | |
{ | |
public DateTime RegistrationDate { get; set; } | |
public DateTime PurchaseDate { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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