using System.Linq.Expressions;
using System.Collections.Generic;
public string Name { get; set; }
public decimal Price { get; set; }
public bool Enabled { get; set; }
public static Expression<Func<Package, bool>> IsActive =>
public DateTimeOffset Start { get; set; }
public DateTimeOffset End { get; set; }
public bool Enabled { get; set; }
public static Expression<Func<Schedule, bool>> IsActive =>
x => x.Enabled && x.Start > DateTimeOffset.Now;
public Package Package { get; init; }
public Schedule Schedule { get; init; }
static Lazy<Expression<Func<SchedulePackage, bool>>> IsActiveExpression = new(static () => {
var paramExpr = Expression.Parameter(typeof(SchedulePackage), "x");
var p = Expression.Property(paramExpr, nameof(SchedulePackage.Package));
var s = Expression.Property(paramExpr, nameof(SchedulePackage.Schedule));
var pIsActive = ExpressionExtensions.UnwrapLambda(Package.IsActive, p);
var sIsActive = ExpressionExtensions.UnwrapLambda(Schedule.IsActive, s);
var andExpression = Expression.MakeBinary(ExpressionType.AndAlso, pIsActive, sIsActive);
var lambdaExpr = Expression.Lambda<Func<SchedulePackage, bool>>(
public static Expression<Func<SchedulePackage, bool>> IsActive => IsActiveExpression.Value;
public static class ExpressionExtensions
class ParameterLambdaUnwrapper : ExpressionVisitor
readonly Dictionary<ParameterExpression, Expression> parametersToReplace;
public ParameterLambdaUnwrapper(params (ParameterExpression parameter, Expression replacement) [] parametersToReplace) =>
this.parametersToReplace = parametersToReplace.ToDictionary(p => p.parameter, p => p.replacement);
public Expression Unwrap(LambdaExpression lambda) =>
protected override Expression VisitParameter(ParameterExpression p) =>
parametersToReplace.TryGetValue(p, out var e) ? e : p;
public static Expression UnwrapLambda<T1, TResult>(Expression<Func<T1, TResult>> lambda, Expression replacement)
=> new ParameterLambdaUnwrapper((lambda.Parameters[0], replacement)).Unwrap(lambda);
public static void Main()
Console.WriteLine($"Schedule.IsActive: {Schedule.IsActive}");
Console.WriteLine($"Package.IsActive : {Package.IsActive}");
Console.WriteLine($"SchedulePackage.IsActive: {SchedulePackage.IsActive}");
Assert.That(!SchedulePackage.IsActive.ToString().Contains("Invoke("));
var schedulePackages = new [] { true, false }
.SelectMany(b => new [] { true, false }, (b1, b2) => (b1, b2))
new SchedulePackage { Schedule = new () { Enabled = p.b1, Start = DateTime.Now.AddDays(4) }, Package = new () { Enabled = p.b2 } })
var items = schedulePackages.Where(SchedulePackage.IsActive);
Console.WriteLine(items.Count());
Assert.AreEqual(1, items.Count());
var isActive = SchedulePackage.IsActive.Compile();
foreach (var enabled1 in new [] { true, false } )
foreach (var enabled2 in new [] { true, false } )
Test(new () { Enabled = enabled1 }, new () { Enabled = enabled2, Start = DateTime.Now.AddDays(4) }, isActive);
static void Test(Package p, Schedule s, Func<SchedulePackage, bool> isActive)
var schedulePackage = new SchedulePackage { Schedule = s, Package = p };
Console.WriteLine($"isActive(schedulePackage) = {isActive(schedulePackage)}");
Assert.AreEqual(Package.IsActive.Compile()(p) && Schedule.IsActive.Compile()(s), isActive(schedulePackage));