I have following code:
public class MyClass
{
Expression> Criteria {get; set;}
}
public class Customer
{
//..
publ
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");
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;}
}
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)
.
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");