Search code examples
c#lambdaexpression-treesfunc

How do I create a generic Expression that has an expression as a parameter


There is a DisplayNameFor(x=>x.Title) helper in ASP.Net MVC. I want to implement something similar in behavior.

I want to have a method that accepts an expression based on User class (u=>u.Birthdate or u=>u.Name), a operand (Greater, Less, Equal) and a value like DateTime.Now and returns an expression u=>u.Birthdate > DateTime.Now

I understand that I'll have to build resulting expression manually from pieces. What i can't wrap my head around is passing in and handling of property expression.

Edit:
I want to call a method like
GetFilterPredicate(u=>u.Birthdate,FilterOps.GreaterThan,DateTime.Parse("01.01.2013") or
GetFilterPredicate(u=>u.SomeIntProperty,FilterOps.Equals,2)

Update: I've created a repo with a solution to this question as well as a collection property filtering https://github.com/Alexander-Taran/Lambda-Magic-Filters


Solution

  • Does this satisfy your needs ?

    [TestClass]
    public class UnitTest1
    {
        public Expression<Predicate<T>> GetFilterPredicate<T, R>(Expression<Func<T, R>> selector, FilterOps operand, R value)
        {
            var parameter = selector.Parameters[0];
    
            var left = selector.Body;
            var right = Expression.Constant(value);
    
            var binaryExpression = Expression.MakeBinary(operand.ToExpressionType(), left, right);
            return Expression.Lambda<Predicate<T>>(binaryExpression, parameter);
        }
    
        [TestMethod]
        public void TestMethod1()
        {
            var p1 = this.GetFilterPredicate((User u) => u.Birthday.TimeOfDay.Hours, FilterOps.LessThan, 12);
            var p2 = this.GetFilterPredicate((User u) => u.Size, FilterOps.Equal, 180);
    
            var user = new User() { Birthday = new DateTime(2000, 1, 1), Size = 180 };
    
            Assert.IsTrue(p1.Compile()(user));
            Assert.IsTrue(p2.Compile()(user));
        }
    }
    
    public enum FilterOps
    {
        GreaterThan, LessThan, Equal
    }
    public static class MyExtensions
    {
        public static ExpressionType ToExpressionType(this FilterOps operand)
        {
            switch (operand)
            {
                case FilterOps.GreaterThan: return ExpressionType.GreaterThan;
                case FilterOps.LessThan: return ExpressionType.LessThan;
                case FilterOps.Equal: return ExpressionType.Equal;
                default: throw new NotSupportedException();
            }
        }
    }
    
    public class User { public DateTime Birthday { get; set; } public int Size { get; set; } }