I need to evaluate some data. The rules how it should be done are changing frequently (it's an evolving model) so I don't want to rewrite my application each time such a change comes. I'd rather do it quickly via a config file.

In order to make this possible I've designed a system of components that are very similar to `C#`'s expression trees and LINQ extensions. They can be put together to be used as decision trees or to calculate other results. It provides a couple of standard operations and needs to be extended by buisiness specific components.

---

### Core

The `Expression` type is the core. All other components are built from here. It's an interface and a class that provides the basic functionality.

    public interface ISwitchable
    {
        [DefaultValue(true)]
        bool Enabled { get; }
    }

    [UsedImplicitly]
    public interface IExpression : ISwitchable
    {
        [NotNull]
        string Name { get; }

        [NotNull]
        IExpression Invoke([NotNull] IExpressionContext context);
    }

    public abstract class Expression : IExpression
    {
        protected Expression(string name) => Name = name;

        public virtual string Name { get; }

        public bool Enabled { get; set; } = true;

        public abstract IExpression Invoke(IExpressionContext context);
    }

The next level is represented by types that help me to implement standard logical operations or calculations:

    public abstract class PredicateExpression : Expression
    {
        protected PredicateExpression(string name) : base(name) { }

        public override IExpression Invoke(IExpressionContext context)
        {
            using (context.Scope(this))
            {
                return Constant.Create(Name, Calculate(context));
            }
        }

        protected abstract bool Calculate(IExpressionContext context);
    }    

    public abstract class AggregateExpression : Expression
    {
        private readonly Func<IEnumerable<double>, double> _aggregate;

        protected AggregateExpression(string name, [NotNull] Func<IEnumerable<double>, double> aggregate) : base(name) => _aggregate = aggregate;

        [JsonRequired]
        public IEnumerable<IExpression> Expressions { get; set; }

        public override IExpression Invoke(IExpressionContext context) => Constant.Create(Name, _aggregate(Expressions.InvokeWithValidation(context).Values<double>().ToList()));
    }

    public abstract class ComparerExpression : Expression
    {
        private readonly Func<int, bool> _predicate;

        protected ComparerExpression(string name, [NotNull] Func<int, bool> predicate) : base(name) => _predicate = predicate;

        [JsonRequired]
        public IExpression Left { get; set; }

        [JsonRequired]
        public IExpression Right { get; set; }

        public override IExpression Invoke(IExpressionContext context)
        {
            var result1 = Left.InvokeWithValidation(context);
            var result2 = Right.InvokeWithValidation(context);

            // optimizations

            if (result1 is Constant<double> d1 && result2 is Constant<double> d2) return Constant.Create(Name, _predicate(d1.Value.CompareTo(d2.Value)));
            if (result1 is Constant<int> i1 && result2 is Constant<int> i2) return Constant.Create(Name, _predicate(i1.Value.CompareTo(i2.Value)));

            // fallback to weak comparer
            var x = (result1 as IConstant)?.Value as IComparable ?? throw new InvalidOperationException($"{nameof(Left)} must return an {nameof(IConstant)} expression with an {nameof(IComparable)} value.");
            var y = (result2 as IConstant)?.Value as IComparable ?? throw new InvalidOperationException($"{nameof(Right)} must return an {nameof(IConstant)} expression with an {nameof(IComparable)} value."); ;
            return Constant.Create(Name, _predicate(x.CompareTo(y)));
        }
    }

---

### Expressions

I use the above base classes to create the actual components with very few lines of code. They mostly use LINQ internally.

    public class All : PredicateExpression
    {
        public All() : base(nameof(All)) { }

        [JsonRequired]
        public IEnumerable<IExpression> Expressions { get; set; }

        protected override bool Calculate(IExpressionContext context)
        {
            return 
                Expressions
                    .Enabled()
                    .InvokeWithValidation(context)
                    .Values<bool>()
                    .All(x => x);
        }
    }

    public class Any : PredicateExpression
    {
        public Any() : base(nameof(Any)) { }

        [JsonRequired]
        public IEnumerable<IExpression> Expressions { get; set; }

        protected override bool Calculate(IExpressionContext context)
        {
            return
                Expressions
                    .Enabled()
                    .InvokeWithValidation(context)
                    .Values<bool>()
                    .Any(x => x);

        }
    }

    public class IIf : Expression
    {
        public IIf() : base(nameof(IIf)) { }

        [JsonRequired]
        public IExpression Predicate { get; set; }

        public IExpression True { get; set; }

        public IExpression False { get; set; }

        public override IExpression Invoke(IExpressionContext context)
        {
            using (context.Scope(this))
            {
                var expression =
                    (Predicate.InvokeWithValidation(context).Value<bool>() ? True : False)
                        ?? throw new InvalidOperationException($"{nameof(True)} or {nameof(False)} expression is not defined."); ;

                return expression.InvokeWithValidation(context);
            }
        }
    }

    public class Min : AggregateExpression
    {
        public Min()
        : base(nameof(Min), Enumerable.Min)
        { }
    }

    public class Max : AggregateExpression
    {
        public Max()
            : base(nameof(Max), Enumerable.Max)
        { }
    }

    public class Sum : AggregateExpression
    {
        public Sum()
        : base(nameof(Sum), Enumerable.Sum)
        { }
    }

    public class Equals : PredicateExpression
    {
        public Equals() : base(nameof(Equals)) { }

        [DefaultValue(true)]
        public bool IgnoreCase { get; set; } = true;

        public IExpression Left { get; set; }

        public IExpression Right { get; set; }

        protected override bool Calculate(IExpressionContext context)
        {
            var x = Left.InvokeWithValidation(context).ValueOrDefault();
            var y = Right.InvokeWithValidation(context).ValueOrDefault();

            if (x is string str1 && y is string str2 && IgnoreCase)
            {
                return StringComparer.OrdinalIgnoreCase.Equals(str1, str2);
            }

            return x.Equals(y);
        }
    }

    public class Matches : PredicateExpression
    {
        protected Matches() : base(nameof(Matches)) { }

        [DefaultValue(true)]
        public bool IgnoreCase { get; set; } = true;

        public IExpression Expression { get; set; }

        public string Pattern { get; set; }

        protected override bool Calculate(IExpressionContext context)
        {
            var x = Expression.InvokeWithValidation(context).Value<string>();

            return !(x is null) && Regex.IsMatch(x, Pattern, IgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None);
        }
    }

    public class GreaterThan : ComparerExpression
    {
        public GreaterThan()
            : base(nameof(GreaterThan), x => x > 0)
        { }
    }

    public class GreaterThanOrEqual : ComparerExpression
    {
        public GreaterThanOrEqual()
            : base(nameof(GreaterThanOrEqual), x => x >= 0)
        { }
    }

    public class LessThan : ComparerExpression
    {
        public LessThan()
            : base(nameof(LessThan), x => x < 0)
        { }
    }

    public class LessThanOrEqual : ComparerExpression
    {
        public LessThanOrEqual()
            : base(nameof(LessThanOrEqual), x => x <= 0)
        { }
    }

    public class Not : PredicateExpression
    {
        public Not() : base(nameof(Not)) { }

        public IExpression Expression { get; set; }

        protected override bool Calculate(IExpressionContext context) => !Expression.InvokeWithValidation(context).Value<bool>();
    }

---

### Constant expression

There is also one very special expression which is the `Constant<T>`. No expression is allowed to return a `null` so they all must return either another expression or a `Constant<T>` expression. A constant is a type that can have a name and must have a `Value`. It also provides a bunch of helper factory methods to reduce the ammount of typing necessary to create them.

    public interface IConstant
    {
        string Name { get; }

        object Value { get; }
    }

    public class Constant<TValue> : Expression, IEquatable<Constant<TValue>>, IConstant
    {
        public Constant(string name) : base(name) { }

        [JsonConstructor]
        public Constant(string name, TValue value) : this(name) => Value = value;

        [AutoEqualityProperty]
        [CanBeNull]
        public TValue Value { get; }

        [CanBeNull]
        object IConstant.Value => Value;

        public override IExpression Invoke(IExpressionContext context)
        {
            using (context.Scope(this))
            {
                return this;
            }
        }

        public override string ToString() => $"\"{Name}\" = \"{Value}\"";

        public static implicit operator Constant<TValue>((string name, TValue value) t) => new Constant<TValue>(t.name, t.value);

        public static implicit operator TValue(Constant<TValue> constant) => constant.Value;

        #region IEquatable

        public override int GetHashCode() => AutoEquality<Constant<TValue>>.Comparer.GetHashCode(this);

        public override bool Equals(object obj) => obj is Constant<TValue> constant && Equals(constant);

        public bool Equals(Constant<TValue> other) => AutoEquality<Constant<TValue>>.Comparer.Equals(this, other);

        #endregion
    }

    public class One : Constant<double>
    {
        public One(string name) : base(name, 1.0) { }
    }

    public class Zero : Constant<double>
    {
        public Zero(string name) : base(name, 0.0) { }
    }

    public class True : Constant<bool>
    {
        public True(string name) : base(name, true) { }
    }

    public class False : Constant<bool>
    {
        public False(string name) : base(name, false) { }
    }

    public class String : Constant<string>
    {
        [JsonConstructor]
        public String(string name, string value) : base(name, value) { }
    }

    /// <summary>
    /// This class provides factory methods.
    /// </summary>
    public class Constant
    {
        private static volatile int _counter;

        public static Constant<TValue> Create<TValue>(string name, TValue value) => new Constant<TValue>(name, value);

        public static Constant<TValue> Create<TValue>(TValue value) => new Constant<TValue>($"{typeof(Constant<TValue>).ToPrettyString()}{_counter++}", value);

        public static IList<Constant<TValue>> CreateMany<TValue>(string name, params TValue[] values) => values.Select(value => Create(name, value)).ToList();

        public static IList<Constant<TValue>> CreateMany<TValue>(params TValue[] values) => values.Select(Create).ToList();
    }

---

### Unit-testing

The `Constant` expression is also a great help for testing. Here are a couple of exmpales (the actual list is much longer):

        [TestMethod]
        public void All_ReturnsTrueWhenAllTrue() => Assert.That.ExpressionsEqual(true, new All { Expressions = Constant.CreateMany(true, true, true) });

        [TestMethod]
        public void All_ReturnsFalseWhenSomeFalse() => Assert.That.ExpressionsEqual(false, new All { Expressions = Constant.CreateMany(true, false, true) });

        [TestMethod]
        public void All_ReturnsFalseWhenAllFalse() => Assert.That.ExpressionsEqual(false, new All { Expressions = Constant.CreateMany(false, false, false) });

        [TestMethod]
        public void Any_ReturnsTrueWhenSomeTrue() => Assert.That.ExpressionsEqual(true, new Any { Expressions = Constant.CreateMany(false, false, true) });

        [TestMethod]
        public void Any_ReturnsFalseWhenAllFalse() => Assert.That.ExpressionsEqual(false, new Any { Expressions = Constant.CreateMany(false, false, false) });

They use my helper extension to reduce code repetition:

    internal static class Helpers
    {
        public static void ExpressionsEqual<TValue, TExpression>(this Assert _, TValue expectedValue, TExpression expression, IExpressionContext context = null) where TExpression : IExpression
        {
            context = context ?? new ExpressionContext();
            var expected = Constant.Create(expression.Name, expectedValue);
            var actual = expression.Invoke(context);

            if (!expected.Equals(actual))
            {
                throw new AssertFailedException(CreateAssertFailedMessage(expected, actual));
            }
        }

        private static string CreateAssertFailedMessage(object expected, object actual)
        {
            return
                $"{Environment.NewLine}" +
                $"» Expected:{Environment.NewLine}{expected}{Environment.NewLine}" +
                $"» Actual:{Environment.NewLine}{actual}" +
                $"{Environment.NewLine}";
        }
    }

---

### Invoking expressions

To _run_ an expression you `Invoke` it by passing the `IExpressionContext`

    public interface IExpressionContext
    {
        [NotNull]
        IDictionary<object, object> Items { get; }        

        [NotNull]
        ExpressionMetadata Metadata { get; }
    }

    public class ExpressionContext : IExpressionContext
    {
        public IDictionary<object, object> Items { get; } = new Dictionary<object, object>();        

        public ExpressionMetadata Metadata { get; } = new ExpressionMetadata();
    }

    public class ExpressionMetadata
    {
        public string DebugView => ExpressionContextScope.Current.ToDebugView();
    }

I usually use this context as a base class for a business context adding other properties to it (e.g. `CarName`)

I borrowed also the idea of `Items` from `ASP.NET Core`'s `HttpContext.Items` and the `Metadata` from `EF Core`. I use the metadata to create a `DebugView` and to see where I am while testing the tree:

---

### Debug helpers

The `ExpressionContextScope` is inspired by the logger scope used in `ASP.NET Core`. Here it maintains the scope of expressions and the extension is used to build a string showing the position in the tree. (This is going to be more complex later and will render more information in to `DebugView`.)

    [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")]
    public class ExpressionContextScope : IDisposable
    {
        // ReSharper disable once InconsistentNaming - This cannot be renamed because it'd confilict with the property that has the same name.
        private static readonly AsyncLocal<ExpressionContextScope> _current = new AsyncLocal<ExpressionContextScope>();

        private ExpressionContextScope(IExpression expression, IExpressionContext context, int depth)
        {
            Expression = expression;
            Context = context;
            Depth = depth;
        }

        private string DebuggerDisplay => this.ToDebuggerDisplayString(builder =>
        {
            builder.Property(x => x.Depth);
        });

        public ExpressionContextScope Parent { get; private set; }

        public static ExpressionContextScope Current
        {
            get => _current.Value;
            private set => _current.Value = value;
        }

        public IExpression Expression { get; }

        public IExpressionContext Context { get; }

        public int Depth { get; }

        public static ExpressionContextScope Push(IExpression expression, IExpressionContext context)
        {
            var scope = Current = new ExpressionContextScope(expression, context, Current?.Depth + 1 ?? 0)
            {
                Parent = Current
            };
            return scope;
        }

        public void Dispose() => Current = Current.Parent;
    }

    public static class ExpressionContextScopeExtensions
    {
        private const int IndentWidth = 4;

        public static string ToDebugView(this ExpressionContextScope scope)
        {
            var scopes = new Stack<ExpressionContextScope>(scope.Flatten());

            var debugView = new StringBuilder();

            foreach (var inner in scopes)
            {
                debugView
                    .Append(IndentString(inner.Depth))
                    .Append(inner.Expression.Name)
                    .Append(inner.Expression is IConstant constant ? $": {constant.Value}"  : default)
                    .AppendLine();
            }

            return debugView.ToString();
        }

        private static string IndentString(int depth) => new string(' ', IndentWidth * depth);

        public static IEnumerable<ExpressionContextScope> Flatten(this ExpressionContextScope scope)
        {
            var current = scope;
            while (current != null)
            {
                yield return current;
                current = current.Parent;
            }
        }
    }

---

### Using data from other expressions

There also a couple of cases where expressions like `TryGetCarColor` are not only used to determine whether a property exists but also should return a value that is used by other expression later.

To make the framework more robust I decorate such expressions with `In` and/or `Out` attibutes that specify which values they expect or return. The in/out data is stored inside `Items`.

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class InAttribute : Attribute, IParameterAttribute
    {
        public InAttribute(string name) => Name = name;

        public string Name { get; }

        public bool Required { get; set; } = true;
    }

    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class OutAttribute : Attribute, IParameterAttribute
    {
        public OutAttribute(string name) => Name = name;

        public string Name { get; }
        
        public bool Required { get; set; } = true;
    }

 The framework validates their existence via

>     .InvokeWithValidation(context)

that checks whether all required in/out items exist:

        public static IEnumerable<IExpression> InvokeWithValidation(this IEnumerable<IExpression> expressions, IExpressionContext context)
        {
            return
                from expression in expressions
                select
                    expression
                        .ValidateInItems(context)
                        .Invoke(context)
                        .ValidateOutItems(context);
        }

This way (if everything is properly decorated) I can be sure that each expression will receive it's data and doesn't need any additional checks.

---

### Example

Here's an example of a real-world epxpression tree. (I've anonymized it by only changing the name of the business specific expressions like `HasColor` etc. the tree by itself is the same.)

As you can see I use it to evaluate a couple of conditions and then based on them perfom a calculation. Business specific expressions such as `HasColor` or `SeatCount` are also derived from `Expression` but they evaluate the business data.

<!-- language: lang-js -->

    {
        "$t:": "IIf",
        "Predicate": {    
            "$t": "Not",
            "Expression": {
                "$t": "Any",
                "Expressions": [
                    {
                        "$t": "All",
                        "Expressions": [
                            {
                                "$t": "HasColor",
                                "Values": [ "Red", "Blue" ]
                            },
                            {
                                "$t": "HasFeature",
                                "Values": [ "PowerSteering" ]
                            }
                        ]
                    },
                    {
                        "$t": "IIf",
                        "Predicate": {
                            "$t": "HasColor",
                            "Values": [ "Red" ]
                        },
                        "True": {
                            "$t": "Not",
                            "Expression": {
                                "$t": "HasFeature",
                                "Values": [ "PowerBrake" ]
                            }
                        },
                        "False": {
                            "$t": "Constant<double>",
                            "Value": 1
                        }
                    }
                ]
            }
        },
        "True": {
            "$t": "Sum",
            "Expressions": [
                {
                    "$t": "Color"
                },
                {
                    "$t": "SeatCount"
                },
                {
                    "$t": "IIf",
                    "Predicate":{
                        "$t": "HasFeature",
                        "Values": [ "PowerBrake" ]                
                    },
                    "True": {
                        "$t": "Constant<double>",
                        "Value": 3,
                    },
                    "False": null
                }
            ]
        },
        "False": null    
    }

This means that in code you'd have:

    var result = carValueExpression.Invoke(new CarStockExpressionContext
    {
        // ... general car data
        // other data can be pulled from a db by any business expression
    }).Value<double>();

---

I find this is very easy to test and to extend because everything can be covered by unit-tests. Knowing that all components work as expected, it's a piece of cake to put them together so that they can do much bigger things.

---

In case you are wondering what those `$t` are and why the types are not named by their full names, I'm using here my json.net [helper](https://codereview.stackexchange.com/questions/205940/making-typenamehandling-in-json-net-more-convenient) for more friendly type handling.

---

What do you think about this framework? Did I forget to implement anything important or could I have done it better?