This is a follow on to Part 1In order to use my new Specifications within expression trees so I can use them in projections/navigation collections I had to write an ExpressionVisitor to find Specifications and replace them with their Expression.In order to convert an IQueryable into one that supports the specifications, I created an Extension Method called AsSpecifiable():public static class QueryableExtensions{    public static IQueryable<T> AsSpecifiable<T>(this IQueryable<T> query)    {        var visitableQuery = query as VisitableQuery<T>;        return visitableQuery ?? new VisitableQuery<T>(query, new SpecificationExpander());    }}Which uses a VisitableQuery:// Based on:// http://tomasp.net/blog/linq-expand.aspx// http://www.albahari.com/nutshell/linqkit.htmlpublic class VisitableQuery<T> : IOrderedQueryable<T>{    private readonly VisitableQueryProvider<T> provider;    internal IQueryable<T> InnerQuery { get; }     internal VisitableQuery(IQueryable<T> inner, params ExpressionVisitor[] visitors)    {        InnerQuery = inner;        provider = new VisitableQueryProvider<T>(this, visitors);    }    Expression IQueryable.Expression => InnerQuery.Expression;    Type IQueryable.ElementType => typeof(T);    IQueryProvider IQueryable.Provider => provider;    public IEnumerator<T> GetEnumerator()    {        return InnerQuery.GetEnumerator();    }    IEnumerator IEnumerable.GetEnumerator()    {        return InnerQuery.GetEnumerator();    }    public override string ToString()    {        return InnerQuery.ToString();    }}And the corresponding QueryProvider:// Based on:// http://tomasp.net/blog/linq-expand.aspx// http://www.albahari.com/nutshell/linqkit.htmlpublic class VisitableQueryProvider<T> : IQueryProvider{    private readonly ExpressionVisitor[] interceptors;    private readonly VisitableQuery<T> query;    internal VisitableQueryProvider(        VisitableQuery<T> query,        ExpressionVisitor[] interceptors)    {        this.query = query;        this.interceptors = interceptors;    }    IQueryable<TElement> IQueryProvider.CreateQuery<TElement>(Expression expression)    {        return            new VisitableQuery<TElement>(                query.InnerQuery.Provider.CreateQuery<TElement>(                    expression.VisitWith(interceptors)));    }    IQueryable IQueryProvider.CreateQuery(Expression expression)    {        return query.InnerQuery.Provider.CreateQuery(expression.VisitWith(interceptors));    }    TResult IQueryProvider.Execute<TResult>(Expression expression)    {        return            query.InnerQuery.Provider.Execute<TResult>(expression.VisitWith(interceptors));    }    object IQueryProvider.Execute(Expression expression)    {        return query.InnerQuery.Provider.Execute(expression.VisitWith(interceptors));    }}And finally the ExpressionVisitor:public class SpecificationExpander : ExpressionVisitor{    protected override Expression VisitUnary(UnaryExpression node)    {        var declaringType = node.Method?.DeclaringType;        if (declaringType != null            && declaringType.IsGenericType            && declaringType.GetGenericTypeDefinition() == typeof(Specification<>))        {            if (node.Method.Name == op_Implicit)            {                var method = typeof(Specification<>).MakeGenericType(declaringType.GetGenericArguments()).GetMethod(nameof(Specification<object>.ToExpression));                if (node.Operand.NodeType == ExpressionType.Not)                {                    var operand = ((UnaryExpression)node.Operand).Operand;                    var expanded = ExpandConversion(Visit(operand), method);                    if (expanded != null)                    {                        var negate = typeof(ExpressionExtensions).GetMethod(nameof(ExpressionExtensions.Negate)).MakeGenericMethod(declaringType.GetGenericArguments());                        return (Expression)negate.Invoke(null, new object[] { expanded }); ;                    }                }                else if (node.Operand.NodeType == ExpressionType.Call)                {                    var expression = Visit(node.Operand);                    var expanded = ExpandConversion(expression, method);                    if (expanded != null)                    {                        return expanded;                    }                }                else                {                    var expanded = ExpandConversion(node.Operand, method);                    if (expanded != null)                    {                        return expanded;                    }                    throw new InvalidOperationException($Cannot convert the specification ({node.Type.Name}) to an expression);                }            }        }        return base.VisitUnary(node);    }    protected override Expression VisitMethodCall(MethodCallExpression node)    {        var declaringType = node.Method.DeclaringType;        if (declaringType != null            && declaringType.IsGenericType            && declaringType.GetGenericTypeDefinition() == typeof(Specification<>))        {            if (node.Method.Name == nameof(Specification<object>.ToExpression))            {                var expanded = ExpandConversion(Visit(node.Object), node.Method);                if (expanded != null)                {                    return expanded;                }            }            else if (node.Method.Name == nameof(Specification<object>.Negate))            {                var value = GetValue((MemberExpression)node.Object);                var negate = typeof(Specification<>).MakeGenericType(declaringType.GetGenericArguments()).GetMethod(nameof(Specification<object>.Negate));                var specification = negate.Invoke(value, null);                return Expression.Constant(specification, specification.GetType());            }            else            {                throw new InvalidOperationException(                    ${node.Method.Name} cannot be used within an expression);            }        }        return base.VisitMethodCall(node);    }    private Expression ExpandConversion(Expression node, MethodInfo method)    {        var newExpression = node as NewExpression;        if (newExpression != null)        {            var parameters = GetArgumentValues(newExpression.Arguments);            var specification = newExpression.Constructor.Invoke(parameters);            return (Expression)method.Invoke(specification, null);        }        var memberExpression = node as MemberExpression;        if (memberExpression != null)        {            return (Expression)method.Invoke(GetValue(memberExpression), null);        }        var constantExpression = node as ConstantExpression;        if (constantExpression != null)        {            return (Expression)method.Invoke(constantExpression.Value, null);        }        return null;    }    private object[] GetArgumentValues(IReadOnlyList<Expression> arguments)    {        if (arguments?.Count == 0)        {            return null;        }        var results = new object[arguments.Count];        for (var i = 0; i < arguments.Count; i++)        {            var constant = arguments[i] as ConstantExpression;            if (constant != null)            {                results[i] = constant.Value;                continue;            }            var member = arguments[i] as MemberExpression;            if (member != null)            {                results[i] = GetValue(member);                continue;            }            throw new InvalidOperationException(Invalid argument to specification constructor.);        }        return results;    }    // http://stackoverflow.com/a/2616980/1402923    public static object GetValue(Expression expression)    {        var objectMember = Expression.Convert(expression, typeof(object));        var getterLambda = Expression.Lambda<Func<object>>(objectMember);        return getterLambda.Compile().Invoke();    }}This question relies on code which is present in part 1 so you'll need to grab all of the code there if you want to run it. That said, here's what it looks like when it's used:var activeMemberAlertFinders = from s in subscriptions.AsSpecifiable()                               from m in s.Members.AsQueryable().Where(new MemberIsActive())                               from a in m.Alerts.AsQueryable().Where(new AlertIsActive())                               select a.Scope;All of this code works but I can't help but feel I massively over complicated the Visitor - I've never written one that does this sort of thing before.How can I improve this code or should I just scrap it and go back to using static fields storing Expressions?EditJust in case it makes life easier, I've put all of the code on GitHub minus the tests which I have yet to migrate away from Machine.Specifications.
Specification pattern in EF part 2
c#;entity framework;expression trees
Looking through the code it's ok. I wouldn't scrap it all and start again as the usage is clean and sensible.The code is readable and as you've based this on a pattern should be maintainable.