Reputation: 3609
How can I make a generic helper method to convert the type used by a Func from one type to another within an Expression
I have a Expression<Func<IEmployee, bool>>
and I want to convert it to a
Expression<Func<Employee, bool>>.
The second Type always implements the first Type. A generic solution is what I am trying to achieve.
Edit
I have edited the question to be clearer.
Upvotes: 2
Views: 1564
Reputation: 113392
Well, you could create an expression that casts and then forwards its argument to the original expression:
Expression<Func<IEmployee, bool>> source = ...
var param = Expression.Parameter(typeof(Employee));
// Types the argument as the type expected by the source expression
// and then forwards it...
var invocationExpr = Expression.Invoke
(source, Expression.TypeAs(param, typeof(IEmployee)));
var result = Expression.Lambda<Func<Employee, bool>>(invocationExpr, param);
If the provider doesn't support invocation expressions, you will probably need a much more sophisticated solution that replaces Parameters in the source expression.
EDIT: Ok, since you say your provider doesn't like the resulting expression, here's an example of the alternative. It's a really rough cut of what a parameter-replacer should look like (I just wrote this now as a sample), but it should work fine for your purposes.
public static class ParameterReplacer
{
// Produces an expression identical to 'expression'
// except with 'source' parameter replaced with 'target' parameter.
public static Expression<TOutput> Replace<TInput, TOutput>
(Expression<TInput> expression,
ParameterExpression source,
ParameterExpression target)
{
return new ParameterReplacerVisitor<TOutput>(source, target)
.VisitAndConvert(expression);
}
private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
{
private ParameterExpression _source;
private ParameterExpression _target;
public ParameterReplacerVisitor
(ParameterExpression source, ParameterExpression target)
{
_source = source;
_target = target;
}
internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
{
return (Expression<TOutput>)VisitLambda(root);
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
// Leave all parameters alone except the one we want to replace.
var parameters = node.Parameters.Select
(p => p == _source ? _target : p);
return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
}
protected override Expression VisitParameter(ParameterExpression node)
{
// Replace the source with the target, visit other params as usual.
return node == _source ? _target : base.VisitParameter(node);
}
}
}
And then use it as:
Expression<Func<IEmployee, bool>> expression = ...
var result = ParameterReplacer.Replace
<Func<IEmployee, bool>, Func<Employee, bool>>
(expression,
expression.Parameters.Single(),
Expression.Parameter(typeof(Employee));
Upvotes: 6
Reputation: 244757
If the second type always inherits from or implements the first type, you can use
Func<TOut, bool> Convert<TIn, TOut>(Func<TIn, bool> f) where TOut : TIn
{
return (TOut x) => f((TIn)x);
}
If there is no such relationship, you can't use generics for this.
Upvotes: 3