Define part of an Expression as a variable in c#

后端 未结 4 513
有刺的猬
有刺的猬 2020-12-19 10:49

I have following code:

public class MyClass
{
   Expression> Criteria {get; set;}
}
public class Customer
{
   //..
   publ         


        
相关标签:
4条回答
  • 2020-12-19 11:02

    If you want an expression, then you can use LinqKit to do the following:

    Expression<Func<Customer, string>> p = x => x.Name;
    
    var c = new MyClass<Customer>();
    
    c.Criteria = x => p.Invoke(x).StartsWith("asd"); //Reuse p expression
    
    c.Criteria = c.Criteria.Expand();
    

    Invoke is an extension method provided by LinqKit that helps you to compose expressions easily.

    After invoking the Expand method, c.Criteria would contain an expression that is exactly the same as if you have done this:

    c.Criteria = x => x.Name.StartsWith("asd");
    
    0 讨论(0)
  • 2020-12-19 11:05

    You must define type variable explicit but next code will help you to solve your scenario:

    // define new expression that get an Order object and returns string value
    Expression<Func<Order, string>> p = x => x.Customer.Name;
    var c = new MyClass<Order>();
    
    // Compile the expression to the Func then invoke it and call extra criteria
    c.Criteria = o => p.Compile().Invoke(o).StartsWith("SomeText");
    

    There is little bit simpler solution without expressions:

    Func<Order, string> p = x => x.Customer.Name;
    var c = new MyClass<Order>();
    c.Criteria = o => p(o).StartsWith("SomeText");
    

    You can also use Func<> instead of Expression<> in MyClass:

    public MyClass<T>
    {
       Func<T,bool> Criteria {get; set;}
    }
    
    0 讨论(0)
  • 2020-12-19 11:18

    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).

    0 讨论(0)
  • 2020-12-19 11:28

    I don't see the benefit of using an Expression here. How about a straight Func?

    public class MyClass<T> 
    {
        public Func<T, string, bool> Criteria { get; set; }
    }
    

    And then...

    var myCustomer = new MyClass<Customer>
    {
        Criteria = (c, s) => c.Name.StartsWith(s)
    };
    
    var customer = new Customer { Name = "Bob" };
    
    var x = myCustomer.Criteria(customer, "B");
    
    0 讨论(0)
提交回复
热议问题