using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq.Expressions;
namespace ExpressionVisitorTests
public static void Main()
var colourRepository = new ColourRepository();
var result = colourRepository.GetWhere(c => c.People.Any(p => p.FirstName == "Peter"));
Console.WriteLine(result.Count());
public DtoColour(string name)
public string Name { get; set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
public DtoPerson(string firstName, string lastName)
FavouriteColours = new Collection<DtoFavouriteColour>();
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection<DtoFavouriteColour> FavouriteColours { get; set; }
public class DtoFavouriteColour
public DtoColour Colour { get; set; }
public DtoPerson Person { get; set; }
public class DomainColour {
public DomainColour(string name)
public string Name { get; set; }
public ICollection<DomainPerson> People { get; set; }
public class DomainPerson {
public DomainPerson(string firstName, string lastName)
Colours = new Collection<DomainColour>();
public string FirstName { get; private set; }
public string LastName { get; private set; }
public ICollection<DomainColour> Colours { get; set; }
public class ColourRepository {
private IList<DtoColour> Colours { get; set; }
public ColourRepository()
var favColours = new Collection<DtoFavouriteColour>
new DtoFavouriteColour() { Person = new DtoPerson("Peter", "Parker") },
new DtoFavouriteColour() { Person = new DtoPerson("John", "Smith") },
new DtoFavouriteColour() { Person = new DtoPerson("Joe", "Blogs") }
Colours = new List<DtoColour>
new DtoColour("Red") { FavouriteColours = favColours },
new DtoColour("Blue") { FavouriteColours = new Collection<DtoFavouriteColour>() },
new DtoColour("Yellow") { FavouriteColours = new Collection<DtoFavouriteColour>() }
public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
var coonvertedPred = MyExpressionVisitor.Convert(predicate);
return Colours.Where(coonvertedPred).Select(c => new DomainColour(c.Name)).ToList();
public class MyExpressionVisitor : ExpressionVisitor
private ReadOnlyCollection<ParameterExpression> _parameters;
public static Func<DtoColour, bool> Convert<T>(Expression<T> root)
var visitor = new MyExpressionVisitor();
var expression = (Expression<Func<DtoColour, bool>>)visitor.Visit(root);
return expression.Compile();
protected override Expression VisitParameter(ParameterExpression node)
var param = _parameters?.FirstOrDefault(p => p.Name == node.Name);
if(node.Type == typeof(DomainColour))
return Expression.Parameter(typeof(DtoColour), node.Name);
if (node.Type == typeof(DomainPerson))
return Expression.Parameter(typeof(DtoFavouriteColour), node.Name);
protected override Expression VisitMethodCall(MethodCallExpression node)
if (node.Method.DeclaringType == typeof(Enumerable) && node.Arguments[0].Type == typeof(ICollection<DomainPerson>))
Expression obj = Visit(node.Object);
IEnumerable<Expression> args = Visit(node.Arguments);
if (obj != node.Object || args != node.Arguments)
var generic = typeof(Enumerable).GetMethods()
.Where(m => m.Name == node.Method.Name)
.Where(m => m.GetParameters().Length == node.Arguments.Count)
var constructed = generic.MakeGenericMethod(typeof(DtoFavouriteColour));
var newNode = Expression.Call(obj, constructed, args);
protected override Expression VisitLambda<T>(Expression<T> node)
var parameters = VisitAndConvert(node.Parameters, "VisitLambda");
_parameters = parameters;
return Expression.Lambda(Visit(node.Body), parameters);
protected override Expression VisitMember(MemberExpression node)
var exp = Visit(node.Expression);
if (node.Member.DeclaringType == typeof(DomainColour))
if (node.Type == typeof(ICollection<DomainPerson>))
return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty("FavouriteColours"));
return Expression.MakeMemberAccess(exp, typeof(DtoColour).GetProperty(node.Member.Name));
if (node.Member.DeclaringType == typeof(DomainPerson))
var nested = Expression.MakeMemberAccess(exp, typeof(DtoFavouriteColour).GetProperty("Person"));
return Expression.MakeMemberAccess(nested, typeof(DtoPerson).GetProperty(node.Member.Name));
return base.VisitMember(node);