Define part of an expression as a variable in C #

I have the following code:

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

and use it like:

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

Is there a 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 the where clause in my linq to entities request (EF code first).

+5
source share
4 answers

You can use the following helper functions (maybe they could give them better names, but that doesn't matter):

 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's see how an example expression

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

can be built of two different parts.

If you

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

then

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

or if you have it instead

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

then

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

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

+2
source

You must determine the type of the variable explicitly, but the following code will help you 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 a slightly 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
source

I do not see the benefits of using Expression here. How about direct 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
source

If you need an expression, 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 easily compose expressions.

After calling the Expand method, c.Criteria will contain an expression that will be exactly the same as if you did it:

 c.Criteria = x => x.Name.StartsWith("asd"); 
0
source

Source: https://habr.com/ru/post/1241482/


All Articles