2

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.

Ani
  • 111,048
  • 26
  • 262
  • 307
David
  • 3,519
  • 1
  • 24
  • 30

2 Answers2

6

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));
Ani
  • 111,048
  • 26
  • 262
  • 307
  • I am using Teleriks OpenAccess and it just doesn't seem to like the resulting expression. – David Aug 13 '11 at 17:16
  • Ok, hang on. Will give you a sample for the parameter replacer. – Ani Aug 13 '11 at 17:30
  • No, getting a null object exception, just looking into it the now and will post back in the next 10 mins. Really appreciate your help. – David Aug 13 '11 at 18:18
  • Ani, the parameter replacer seems to be doing its job ok but for whatever reason Teleriks OpenAccess throws a 'Object Reference not set to an instance of object' exception. Real pain I so want what I am doing to work, that is passing a specification to a repository for execution. – David Aug 13 '11 at 18:29
  • Good luck with that. Post another question if required. :) – Ani Aug 13 '11 at 18:33
  • Posted a related question, as I think the issue might be with the parameter replacer. – David Aug 14 '11 at 11:56
3

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.

svick
  • 236,525
  • 50
  • 385
  • 514
  • Sorry, I should have mentioned that yes, the second type always implements the first type. – David Aug 13 '11 at 15:15
  • What if I am using the Func within an Expression? – David Aug 13 '11 at 15:18
  • If you have `Expression>`, then you *don't have* `Func` and you should have said so in the question, because that's a completely different question. – svick Aug 13 '11 at 15:25
  • Sorry for not being completely clear on that, the question header did mention convert Expression Func, the content not being as clear as it could have. – David Aug 13 '11 at 15:28