Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Define part of an Expression as a variable in c#

Tags:

c#

expression

I have following code:

public class MyClass<T>
{
   Expression<Func<T,bool>> Criteria {get; set;}
}
public class Customer
{
   //..
   public string Name {get; set;}
} 

and use it as following:

var c = new MyClass<Customer>();
c.Criteria = x.Name.StartWith("SomeTexts");

Is there any way to define something like this:

? p = x=>x.Customer.Name;
var c = new MyClass<Customer>();
c.Criteria = p => p.StartWith("SomeTexts");

I used Expression<Func<T,bool>> to use it as where clause in my linq to entities query (EF code first).

like image 737
Masoud Avatar asked Oct 31 '22 10:10

Masoud


1 Answers

You can use the following helper functions (one could probably give them a better names, but that's not essential):

public static class ExpressionUtils
{
    public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
    {
        var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
        var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
        return lambda;
    }

    public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
    {
        return innerSelector.Bind(source);
    }

    class ParameterExpressionReplacer : ExpressionVisitor
    {
        public ParameterExpression source;
        public Expression target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == source ? target : base.VisitParameter(node);
        }
    }
}

Let see how the sample expression

c.Criteria = x => x.Name.StartsWith("SomeTexts");

can be built from the two different parts.

If you have

Expression<Func<Customer, string>> e = x => x.Name;

then

c.Criteria = e.Bind(x => x.StartsWith("SomeTexts"));

or if you have this instead

Expression<Func<string, bool>> e = x => x.StartsWith("SomeTexts");

then

c.Criteria = e.ApplyTo((Customer x) => x.Name);

If you have both expressions, then you can use any of the two functions, since a.Bind(b) is equivalent to b.ApplyTo(a).

like image 71
Ivan Stoev Avatar answered Nov 15 '22 04:11

Ivan Stoev