51

Intro

In the application I 'm currently working on, there are two kinds of each business object: the "ActiveRecord" kind and the "DataContract" kind. So for example, there would be:

namespace ActiveRecord {
    class Widget {
        public int Id { get; set; }
    }
}

namespace DataContract {
    class Widget {
        public int Id { get; set; }
    }
}

The database access layer takes care of translating between families: you can tell it to update a DataContract.Widget and it will magically create an ActiveRecord.Widget with the same property values and save that instead.

The problem surfaced when attempting to refactor this database access layer.

The Problem

I want to add methods like the following to the database access layer:

// Widget is DataContract.Widget

interface IDbAccessLayer {
    IEnumerable<Widget> GetMany(Expression<Func<Widget, bool>> predicate);
}

The above is a simple general-use "get" method with custom predicate. The only point of interest is that I am passing in an expression tree instead of a lambda because inside IDbAccessLayer I am querying an IQueryable<ActiveRecord.Widget>; to do that efficiently (think LINQ to SQL) I need to pass in an expression tree so this method asks for just that.

The snag: the parameter needs to be magically transformed from an Expression<Func<DataContract.Widget, bool>> to an Expression<Func<ActiveRecord.Widget, bool>>.

Attempted Solution

What I 'd like to do inside GetMany is:

IEnumerable<DataContract.Widget> GetMany(
    Expression<Func<DataContract.Widget, bool>> predicate)
{
    var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
        predicate.Body,
        predicate.Parameters);

    // use lambda to query ActiveRecord.Widget and return some value
}

This won't work because in a typical scenario, for example if:

predicate == w => w.Id == 0;

...the expression tree contains a MemberAccessExpression instance which has a property of type MemberInfo that describes DataContract.Widget.Id. There are also ParameterExpression instances both in the expression tree and in its parameter collection (predicate.Parameters) that describe DataContract.Widget; all of this will result in errors since the queryable body does not contain that type of widget but rather ActiveRecord.Widget.

After searching a bit, I found System.Linq.Expressions.ExpressionVisitor (its source can be found here in the context of a how-to), which offers a convenient way to modify an expression tree. In .NET 4, this class is included out of the box.

Armed with this, I implemented a visitor. This simple visitor only takes care of changing the types in member access and parameter expressions, but that's enough functionality to work with the predicate w => w.Id == 0.

internal class Visitor : ExpressionVisitor
{
    private readonly Func<Type, Type> typeConverter;

    public Visitor(Func<Type, Type> typeConverter)
    {
        this.typeConverter = typeConverter;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        var dataContractType = node.Member.ReflectedType;
        var activeRecordType = this.typeConverter(dataContractType);

        var converted = Expression.MakeMemberAccess(
            base.Visit(node.Expression),
            activeRecordType.GetProperty(node.Member.Name));

        return converted;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        var dataContractType = node.Type;
        var activeRecordType = this.typeConverter(dataContractType);

        return Expression.Parameter(activeRecordType, node.Name);
    }
}

With this visitor, GetMany becomes:

IEnumerable<DataContract.Widget> GetMany(
    Expression<Func<DataContract.Widget, bool>> predicate)
{
    var visitor = new Visitor(...);
    var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
        visitor.Visit(predicate.Body),
        predicate.Parameters.Select(p => visitor.Visit(p));

    var widgets = ActiveRecord.Widget.Repository().Where(lambda);

    // This is just for reference, see below
    Expression<Func<ActiveRecord.Widget, bool>> referenceLambda = 
        w => w.Id == 0;

    // Here we 'd convert the widgets to instances of DataContract.Widget and
    // return them -- this has nothing to do with the question though.
}

Results

The good news is that lambda is constructed just fine. The bad news is that it isn't working; it's blowing up on me when I try to use it, and the exception messages are really not helpful at all.

I have examined the lambda my code produces and a hardcoded lambda with the same expression; they look exactly the same. I spent hours in the debugger trying to find some difference, but I can't.

When the predicate is w => w.Id == 0, lambda looks exactly like referenceLambda. But the latter works with e.g. IQueryable<T>.Where, while the former does not; I have tried this in the immediate window of the debugger.

I should also mention that when the predicate is w => true, everything works fine. Therefore I am assuming that I 'm not doing enough work in the visitor, but I can't find any more leads to follow.

Final Solution

After taking into account the correct answers to the problem (two of them below; one short, one with code) the problem was solved; I put the code along with a few important notes in a separate answer to keep this long question from becoming even longer.

Thanks to everyone for your answers and comments!

Community
  • 1
  • 1
Jon
  • 428,835
  • 81
  • 738
  • 806
  • Can I ask why you are implementing your own data access layer and not using or improving one of the many open source solutions already out there? – David Perlman May 09 '10 at 10:41
  • The "ActiveRecord" part is SubSonic (which looks neat but don't bother if your 're not using MSSQL). The "DataContract" part is necessary because we need to work with business objects that can be tweaked to purpose (SubSonic generates code; generated code and custom tweaks don't mix). The "translation" between the two was a necessary evil. Of course all this is beside the point... – Jon May 09 '10 at 11:10
  • 2
    Do you get the InvalidOperationException with message "variable 'w' of type 'ConsoleApplication1.Product2' referenced from scope '', but it is not defined" or some other? – TcKs May 09 '10 at 21:02
  • This is the one I 'm getting when the query provider is LINQ to Objects. When it's LINQ to SQL it is different (SubSonic throws NotSupportedException) and the error is "detected" at totally the wrong place. The relevant part of SubSonic's source is where it transforms property accesses like `w => w.Name.Length` to `SELECT CHAR_LENGTH(Name)` -- class `SubSonic.Linq.Translation.MySql.MySqlFormatter`. – Jon May 09 '10 at 22:34
  • Would you mind adding the change to your code that Diego V suggested? This was a great question and it would help a lot of people. – David Robbins May 11 '10 at 10:08
  • @David Robbins, added another section to the question with my code as it stands now and an explanation of the tricky part. – Jon May 11 '10 at 12:10
  • @Jon Great - glad this worked out for you. – David Robbins May 11 '10 at 13:05
  • Brilliant question, and a great solution. – Paul Suart Oct 04 '11 at 21:42
  • Hi, how would you adapt this answer to .NET 3.5? The problem is in the `TransformPredicateLambda()` method that is using `lambda.Name` and `lambda.TailCall`... – Lorenzo Mar 21 '12 at 13:02
  • 1
    @Lorenzo: Simply remove any reference to them; you will be using [this overload](http://msdn.microsoft.com/en-us/library/bb340145%28v=vs.90%29.aspx) of `Expression.Lambda` which (logically) does not take these parameters. – Jon Mar 21 '12 at 13:29
  • I just added my answer to a similar question here: http://stackoverflow.com/questions/7424501/automapper-for-funcs-between-selector-types/7425211#7425211 – luksan Mar 31 '13 at 04:18

6 Answers6

15

It seems you're generating the parameter expression twice, in VisitMember() here:

var converted = Expression.MakeMemberAccess(
    base.Visit(node.Expression),
    activeRecordType.GetProperty(node.Member.Name));

...since base.Visit() will end up in VisitParameter I imagine, and in GetMany() itself:

var lambda = Expression.Lambda<Func<ActiveRecord.Widget, bool>>(
    visitor.Visit(predicate.Body),
    predicate.Parameters.Select(p => visitor.Visit(p));

If you're using a ParameterExpression in the body, it has to be the same instance (not just the same type and name) as the one declared for the Lambda. I've had problems before with this kind of scenario, though I think the result was that I just wasn't able to create the expression, it would just throw an exception. In any case you might try reusing the parameter instance see if it helps.

Diego Veralli
  • 1,011
  • 6
  • 13
  • Your answer looks very promising, as the simple `w => w.Id == 0` does use a `ParameterExpression` in the body, and I totally did not think that they may need to be the same instance (WTF? documentation???). I will investigate and get back to you. Thanks! – Jon May 09 '10 at 22:37
  • @diegov - +1 - that's what I think is wrong. What I would do, however, is to feed the new ParameterExpression into the Visitor on construction, and *never* construct it inside the visitor. Whenever it finds an instance of the 'old' parameter, it subsitutes it with the new one. – Andras Zoltan May 10 '10 at 08:20
  • @diegov - confirmed: this is what the problem was! Thanks! Out of curiosity: how did you know that it has to be the same instance? :-) – Jon May 10 '10 at 19:48
  • I just checked and it was mostly luck actually, the issue was with compiling the expression, not just buildng it as I wrongly recalled, and I presume subsonic doesn't compile it. But it's probably working off the assumption that it is the same instance, and that might be the cause of the cryptic exception.. – Diego Veralli May 10 '10 at 21:29
  • Luck or not it was a very keen observation! You 're right that SubSonic doesn't compile the expression -- instead it takes it apart to build SQL instead, that's the whole point there. – Jon May 11 '10 at 12:08
  • Thank you for saving me my afternoon. I was at a complete loss as to what was going on. – mkedobbs Mar 01 '11 at 19:32
14

It turned out that the tricky part is simply that the ParameterExpression instances that exist in the expression tree of the new lambda must be the same instances as are passed in the IEnumerable<ParameterExpression> parameter of Expression.Lambda.

Note that inside TransformPredicateLambda I am giving t => typeof(TNewTarget) as the "type converter" function; that's because in this specific case, we can assume that all parameters and member accesses will be of that one specific type. More advanced scenarios may need additional logic in there.

The code:

internal class DbAccessLayer {
    private static Expression<Func<TNewTarget, bool>> 
    TransformPredicateLambda<TOldTarget, TNewTarget>(
    Expression<Func<TOldTarget, bool>> predicate)
    {
        var lambda = (LambdaExpression) predicate;
        if (lambda == null) {
            throw new NotSupportedException();
        }

        var mutator = new ExpressionTargetTypeMutator(t => typeof(TNewTarget));
        var explorer = new ExpressionTreeExplorer();
        var converted = mutator.Visit(predicate.Body);

        return Expression.Lambda<Func<TNewTarget, bool>>(
            converted,
            lambda.Name,
            lambda.TailCall,
            explorer.Explore(converted).OfType<ParameterExpression>());
    }


    private class ExpressionTargetTypeMutator : ExpressionVisitor
    {
        private readonly Func<Type, Type> typeConverter;

        public ExpressionTargetTypeMutator(Func<Type, Type> typeConverter)
        {
            this.typeConverter = typeConverter;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            var dataContractType = node.Member.ReflectedType;
            var activeRecordType = this.typeConverter(dataContractType);

            var converted = Expression.MakeMemberAccess(
                base.Visit(node.Expression), 
                activeRecordType.GetProperty(node.Member.Name));

            return converted;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            var dataContractType = node.Type;
            var activeRecordType = this.typeConverter(dataContractType);

            return Expression.Parameter(activeRecordType, node.Name);
        }
    }
}

/// <summary>
/// Utility class for the traversal of expression trees.
/// </summary>
public class ExpressionTreeExplorer
{
    private readonly Visitor visitor = new Visitor();

    /// <summary>
    /// Returns the enumerable collection of expressions that comprise
    /// the expression tree rooted at the specified node.
    /// </summary>
    /// <param name="node">The node.</param>
    /// <returns>
    /// The enumerable collection of expressions that comprise the expression tree.
    /// </returns>
    public IEnumerable<Expression> Explore(Expression node)
    {
        return this.visitor.Explore(node);
    }

    private class Visitor : ExpressionVisitor
    {
        private readonly List<Expression> expressions = new List<Expression>();

        protected override Expression VisitBinary(BinaryExpression node)
        {
            this.expressions.Add(node);
            return base.VisitBinary(node);
        }

        protected override Expression VisitBlock(BlockExpression node)
        {
            this.expressions.Add(node);
            return base.VisitBlock(node);
        }

        protected override Expression VisitConditional(ConditionalExpression node)
        {
            this.expressions.Add(node);
            return base.VisitConditional(node);
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            this.expressions.Add(node);
            return base.VisitConstant(node);
        }

        protected override Expression VisitDebugInfo(DebugInfoExpression node)
        {
            this.expressions.Add(node);
            return base.VisitDebugInfo(node);
        }

        protected override Expression VisitDefault(DefaultExpression node)
        {
            this.expressions.Add(node);
            return base.VisitDefault(node);
        }

        protected override Expression VisitDynamic(DynamicExpression node)
        {
            this.expressions.Add(node);
            return base.VisitDynamic(node);
        }

        protected override Expression VisitExtension(Expression node)
        {
            this.expressions.Add(node);
            return base.VisitExtension(node);
        }

        protected override Expression VisitGoto(GotoExpression node)
        {
            this.expressions.Add(node);
            return base.VisitGoto(node);
        }

        protected override Expression VisitIndex(IndexExpression node)
        {
            this.expressions.Add(node);
            return base.VisitIndex(node);
        }

        protected override Expression VisitInvocation(InvocationExpression node)
        {
            this.expressions.Add(node);
            return base.VisitInvocation(node);
        }

        protected override Expression VisitLabel(LabelExpression node)
        {
            this.expressions.Add(node);
            return base.VisitLabel(node);
        }

        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            this.expressions.Add(node);
            return base.VisitLambda(node);
        }

        protected override Expression VisitListInit(ListInitExpression node)
        {
            this.expressions.Add(node);
            return base.VisitListInit(node);
        }

        protected override Expression VisitLoop(LoopExpression node)
        {
            this.expressions.Add(node);
            return base.VisitLoop(node);
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            this.expressions.Add(node);
            return base.VisitMember(node);
        }

        protected override Expression VisitMemberInit(MemberInitExpression node)
        {
            this.expressions.Add(node);
            return base.VisitMemberInit(node);
        }

        protected override Expression VisitMethodCall(MethodCallExpression node)
        {
            this.expressions.Add(node);
            return base.VisitMethodCall(node);
        }

        protected override Expression VisitNew(NewExpression node)
        {
            this.expressions.Add(node);
            return base.VisitNew(node);
        }

        protected override Expression VisitNewArray(NewArrayExpression node)
        {
            this.expressions.Add(node);
            return base.VisitNewArray(node);
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            this.expressions.Add(node);
            return base.VisitParameter(node);
        }

        protected override Expression VisitRuntimeVariables(RuntimeVariablesExpression node)
        {
            this.expressions.Add(node);
            return base.VisitRuntimeVariables(node);
        }

        protected override Expression VisitSwitch(SwitchExpression node)
        {
            this.expressions.Add(node);
            return base.VisitSwitch(node);
        }

        protected override Expression VisitTry(TryExpression node)
        {
            this.expressions.Add(node);
            return base.VisitTry(node);
        }

        protected override Expression VisitTypeBinary(TypeBinaryExpression node)
        {
            this.expressions.Add(node);
            return base.VisitTypeBinary(node);
        }

        protected override Expression VisitUnary(UnaryExpression node)
        {
            this.expressions.Add(node);
            return base.VisitUnary(node);
        }

        public IEnumerable<Expression> Explore(Expression node)
        {
            this.expressions.Clear();
            this.Visit(node);
            return expressions.ToArray();
        }
    }
}
Jon
  • 428,835
  • 81
  • 738
  • 806
  • I thinks there's a typo in the `ExpressionTargetTypeMutator` class. In fact there's no `VisitMember` method in the base class to override. Moreover, I have implemented a solution that uses your code exactly as is and found a little problem. If my expression is `w => w.Code == 1` then everythings works fine but it does'nt work if I have `w => w.Code == tmpVar` where tmpVar has a value of 1. Is it right or simply I am confusing something? P.S. Anyway, great code, thanks for sharing! – Lorenzo May 11 '12 at 10:11
  • @Lorenzo: There is a [VisitMember](http://msdn.microsoft.com/en-us/library/system.linq.expressions.expressionvisitor.visitmember.aspx) on the .NET 4 class. And actually the *really* final solution does more than what I posted here, but it's much longer and involved. This version wouldn't work with captured variables. – Jon May 11 '12 at 10:21
  • @Lorenzo: I think it's called `VisitMemberAccess` there. – Jon May 11 '12 at 10:33
  • Yes, you're right. I meant advices on make it working with captured variables :) – Lorenzo May 11 '12 at 10:43
  • 1
    @Lorenzo: You will want to read [this](http://blogs.msdn.com/b/mattwar/archive/2007/08/01/linq-building-an-iqueryable-provider-part-iii.aspx). – Jon May 11 '12 at 10:58
  • Thanks! I [expanded your answer](http://stackoverflow.com/a/11997136/562906) to deal with a wider variety of expressions. – sinelaw Aug 17 '12 at 00:06
  • Hello, I have found a small problem that happens on `ExpressionTreeExplorer` when you have more than one condition in the input expression. Let's say you have an expression like this: `m => m.Field1 == 123 && m.Field2 == "xyz"`. In this case `explorer.Explore(converted).OfType()` will return two `ParameterExpression` and this results into an `ArgumentException` with the error message: `Incorrect number of parameters supplied for lambda declaration`. Please note that both ParameterExpression are bound to the `TNewTarget` type. How do I can get rid of this error? – Lorenzo Dec 13 '12 at 13:46
  • @Lorenzo: You will have to swap the simplistic `.OfType()` with something more sophisticated that recognizes multiple parameter expressions that refer to the same parameter (in your example, to `m`) and only returns one copy. Sinelaw's answer below does this, although I cannot tell how bulletproof it is by just skimming the code. In general, the matter is *very* complicated so even though the answers here look involved, in reality they are only prepared to handle the easy parts. – Jon Dec 13 '12 at 13:58
  • Ok. I can understand. I have just one more question: supposing to use Sinelaw's code, which I have adapted to .NET 3.5, how can I create a Lambda object to return, now that it's not needed to use `ExpressionTreeExplorer`? Thanks a lot for your help – Lorenzo Dec 13 '12 at 15:33
  • I think you can simplify your `ExpressionTreeExplorer.Visitor` by a lot if you override `Visit(Expression)` and nothing else. – svick Feb 02 '13 at 20:45
6

I tried the simple (not complete) implementation for mutating the expression p => p.Id == 15 (the code is below). There are one class named "CrossMapping" which defines the mapping between original and "new" types and type members.

There are several metods named Mutate_XY_Expression for every expression type, which makes new mutated expression. The method inputs need the original express (MemberExpression originalExpression) as model of expression, the list or parameters expression (IList<ParameterExpression> parameterExpressions) which are defined parameters by "parent" expression and should be used by "parent's" body, and the mapping object (CrossMapping mapping) which defines the mapping between types and members.

For full implementation you will maybe need more informations from parent's expression than parameters. But the pattern should be same.

Sample does not implement the Visitor pattern, as you know - it's because simplicity. But there is no barrier to converting to them.

I hope, it will help.

The code (C# 4.0):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Linq.Expressions;

namespace ConsoleApplication1 {
    public class Product1 {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Weight { get; set; }
    }

    public class Product2 {
        public int Id { get; set; }
        public string Name { get; set; }
        public decimal Weight { get; set; }
    }

    class Program {
        static void Main( string[] args ) {
            // list of products typed as Product1
            var lst1 = new List<Product1> {
                new Product1{ Id = 1, Name = "One" },
                new Product1{ Id = 15, Name = "Fifteen" },
                new Product1{ Id = 9, Name = "Nine" }
            };

            // the expression for filtering products
            // typed as Product1
            Expression<Func<Product1, bool>> q1;
            q1 = p => p.Id == 15;

            // list of products typed as Product2
            var lst2 = new List<Product2> {
                new Product2{ Id = 1, Name = "One" },
                new Product2{ Id = 15, Name = "Fifteen" },
                new Product2{ Id = 9, Name = "Nine" }
            };

            // type of Product1
            var tp1 = typeof( Product1 );
            // property info of "Id" property from type Product1
            var tp1Id = tp1.GetProperty( "Id", BindingFlags.Public | BindingFlags.Instance );
            // delegate type for predicating for Product1
            var tp1FuncBool = typeof( Func<,> ).MakeGenericType( tp1, typeof( bool ) );

            // type of Product2
            var tp2 = typeof( Product2 );
            // property info of "Id" property from type Product2
            var tp21Id = tp2.GetProperty( "Id", BindingFlags.Public | BindingFlags.Instance );
            // delegate type for predicating for Product2
            var tp2FuncBool = typeof( Func<,> ).MakeGenericType( tp2, typeof( bool ) );

            // mapping object for types and type members
            var cm1 = new CrossMapping {
                TypeMapping = {
                    // Product1 -> Product2
                    { tp1, tp2 },
                    // Func<Product1, bool> -> Func<Product2, bool>
                    { tp1FuncBool, tp2FuncBool }
                },
                MemberMapping = {
                    // Product1.Id -> Product2.Id
                    { tp1Id, tp21Id }
                }
            };

            // mutate express from Product1's "enviroment" to Product2's "enviroment"
            var cq1_2 = MutateExpression( q1, cm1 );

            // compile lambda to delegate
            var dlg1_2 = ((LambdaExpression)cq1_2).Compile();

            // executing delegate
            var rslt1_2 = lst2.Where( (Func<Product2, bool>)dlg1_2 ).ToList();

            return;
        }

        class CrossMapping {
            public IDictionary<Type, Type> TypeMapping { get; private set; }
            public IDictionary<MemberInfo, MemberInfo> MemberMapping { get; private set; }

            public CrossMapping() {
                this.TypeMapping = new Dictionary<Type, Type>();
                this.MemberMapping = new Dictionary<MemberInfo, MemberInfo>();
            }
        }
        static Expression MutateExpression( Expression originalExpression, CrossMapping mapping ) {
            var ret = MutateExpression(
                originalExpression: originalExpression,
                parameterExpressions: null,
                mapping: mapping
            );

            return ret;
        }
        static Expression MutateExpression( Expression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            Expression ret;

            if ( null == originalExpression ) {
                ret = null;
            }
            else if ( originalExpression is LambdaExpression ) {
                ret = MutateLambdaExpression( (LambdaExpression)originalExpression, parameterExpressions, mapping );
            }
            else if ( originalExpression is BinaryExpression ) {
                ret = MutateBinaryExpression( (BinaryExpression)originalExpression, parameterExpressions, mapping );
            }
            else if ( originalExpression is ParameterExpression ) {
                ret = MutateParameterExpression( (ParameterExpression)originalExpression, parameterExpressions, mapping );
            }
            else if ( originalExpression is MemberExpression ) {
                ret = MutateMemberExpression( (MemberExpression)originalExpression, parameterExpressions, mapping );
            }
            else if ( originalExpression is ConstantExpression ) {
                ret = MutateConstantExpression( (ConstantExpression)originalExpression, parameterExpressions, mapping );
            }
            else {
                throw new NotImplementedException();
            }

            return ret;
        }

        static Type MutateType( Type originalType, IDictionary<Type, Type> typeMapping ) {
            if ( null == originalType ) { return null; }

            Type ret;
            typeMapping.TryGetValue( originalType, out ret );
            if ( null == ret ) { ret = originalType; }

            return ret;
        }
        static MemberInfo MutateMember( MemberInfo originalMember, IDictionary<MemberInfo, MemberInfo> memberMapping ) {
            if ( null == originalMember ) { return null; }

            MemberInfo ret;
            memberMapping.TryGetValue( originalMember, out ret );
            if ( null == ret ) { ret = originalMember; }

            return ret;
        }
        static LambdaExpression MutateLambdaExpression( LambdaExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            if ( null == originalExpression ) { return null; }

            var newParameters = (from p in originalExpression.Parameters
                                 let np = MutateParameterExpression( p, parameterExpressions, mapping )
                                 select np).ToArray();

            var newBody = MutateExpression( originalExpression.Body, newParameters, mapping );

            var newType = MutateType( originalExpression.Type, mapping.TypeMapping );

            var ret = Expression.Lambda(
                delegateType: newType,
                body: newBody,
                name: originalExpression.Name,
                tailCall: originalExpression.TailCall,
                parameters: newParameters
            );

            return ret;
        }
        static BinaryExpression MutateBinaryExpression( BinaryExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            if ( null == originalExpression ) { return null; }

            var newExprConversion = MutateExpression( originalExpression.Conversion, parameterExpressions, mapping );
            var newExprLambdaConversion = (LambdaExpression)newExprConversion;
            var newExprLeft = MutateExpression( originalExpression.Left, parameterExpressions, mapping );
            var newExprRigth = MutateExpression( originalExpression.Right, parameterExpressions, mapping );
            var newType = MutateType( originalExpression.Type, mapping.TypeMapping );
            var newMember = MutateMember( originalExpression.Method, mapping.MemberMapping);
            var newMethod = (MethodInfo)newMember;

            var ret = Expression.MakeBinary(
                binaryType: originalExpression.NodeType,
                left: newExprLeft,
                right: newExprRigth,
                liftToNull: originalExpression.IsLiftedToNull,
                method: newMethod,
                conversion: newExprLambdaConversion
            );

            return ret;
        }
        static ParameterExpression MutateParameterExpression( ParameterExpression originalExpresion, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            if ( null == originalExpresion ) { return null; }

            ParameterExpression ret = null;
            if ( null != parameterExpressions ) {
                ret = (from p in parameterExpressions
                       where p.Name == originalExpresion.Name
                       select p).FirstOrDefault();
            }

            if ( null == ret ) {
                var newType = MutateType( originalExpresion.Type, mapping.TypeMapping );

                ret = Expression.Parameter( newType, originalExpresion.Name );
            }

            return ret;
        }
        static MemberExpression MutateMemberExpression( MemberExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            if ( null == originalExpression ) { return null; }

            var newExpression = MutateExpression( originalExpression.Expression, parameterExpressions, mapping );
            var newMember = MutateMember( originalExpression.Member, mapping.MemberMapping );

            var ret = Expression.MakeMemberAccess(
                expression: newExpression,
                member: newMember
            );

            return ret;
        }
        static ConstantExpression MutateConstantExpression( ConstantExpression originalExpression, IList<ParameterExpression> parameterExpressions, CrossMapping mapping ) {
            if ( null == originalExpression ) { return null; }

            var newType = MutateType( originalExpression.Type, mapping.TypeMapping );
            var newValue = originalExpression.Value;

            var ret = Expression.Constant(
                value: newValue,
                type: newType
            );

            return ret;
        }
    }
}
TcKs
  • 25,849
  • 11
  • 66
  • 104
  • Thanks for taking the time to write your own pet program to illustrate. Regretfully, I can't accept both you and diegov as an answer... diegov was first and dead on, so I accepted his. I 'd upvote you guys more if I could :) – Jon May 10 '10 at 19:51
  • Would this also be valid for C# 3.0? – David Robbins May 11 '10 at 13:12
  • 2
    @David: if you replace the named parameters in method calling (var ret = MutateExpression( originalExpression: originalExpression, parameterExpressions: null, mapping: mapping );) to (var ret = MutateExpression( originalExpression, null, mapping );), then you can use it in C# 3.0, – TcKs May 11 '10 at 14:09
5

Jon's own answer above is great, so I expanded it to handle method calls, constant expressions, etc. so that now it works also for expressions such as:

x => x.SubObjects
      .AsQueryable()
      .SelectMany(y => y.GrandChildObjects)
      .Any(z => z.Value == 3)

I also did away with the ExpressionTreeExplorer since the only thing we need are the ParameterExpressions.

Here's the code (Update: Clear the cache when done converting)

public class ExpressionTargetTypeMutator : ExpressionVisitor
{
    private readonly Func<Type, Type> typeConverter;
    private readonly Dictionary<Expression, Expression> _convertedExpressions 
        = new Dictionary<Expression, Expression>();

    public ExpressionTargetTypeMutator(Func<Type, Type> typeConverter)
    {
        this.typeConverter = typeConverter;
    }

    // Clear the ParameterExpression cache between calls to Visit.
    // Not thread safe, but you can probably fix it easily.
    public override Expression Visit(Expression node)
    {
        bool outermostCall = false;
        if (false == _isVisiting)
        {
            this._isVisiting = true;
            outermostCall = true;
        }
        try
        {
            return base.Visit(node);
        }
        finally
        {
            if (outermostCall)
            {
                this._isVisiting = false;
                _convertedExpressions.Clear();
            }
        }
    }


    protected override Expression VisitMember(MemberExpression node)
    {
        var sourceType = node.Member.ReflectedType;
        var targetType = this.typeConverter(sourceType);

        var converted = Expression.MakeMemberAccess(
            base.Visit(node.Expression),
            targetType.GetProperty(node.Member.Name));

        return converted;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        Expression converted;
        if (false == _convertedExpressions.TryGetValue(node, out converted))
        {
            var sourceType = node.Type;
            var targetType = this.typeConverter(sourceType);
            converted = Expression.Parameter(targetType, node.Name);
            _convertedExpressions.Add(node, converted);
        }
        return converted;
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Method.IsGenericMethod)
        {
            var convertedTypeArguments = node.Method.GetGenericArguments()
                                                    .Select(this.typeConverter)
                                                    .ToArray();
            var genericMethodDefinition = node.Method.GetGenericMethodDefinition();
            var newMethod = genericMethodDefinition.MakeGenericMethod(convertedTypeArguments);
            return Expression.Call(newMethod, node.Arguments.Select(this.Visit));
        }
        return base.VisitMethodCall(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {
        var valueExpression = node.Value as Expression;
        if (null != valueExpression)
        {
            return Expression.Constant(this.Visit(valueExpression));
        }
        return base.VisitConstant(node);
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        return Expression.Lambda(this.Visit(node.Body), node.Name, node.TailCall, node.Parameters.Select(x => (ParameterExpression)this.VisitParameter(x)));
    }
}
Community
  • 1
  • 1
sinelaw
  • 16,205
  • 3
  • 49
  • 80
  • I have tried to adapt your code to .NET 3.5 and this ended up by just removing the `VisitLambda()` method. While doing this I have discovered that this code, like the accepted answer as well, runs into problems if the original expression is like this one for example: `x => x.Property1.Property2.Property3 == 1`. The problem happens into `MakeMemberAccess()` method when it tries to convert a 'navigated' type (e.g. Property2) to an original type due its typeConverter member variable. Could you please drive me in resolving this issue as I am not really strong with expressions? – Lorenzo Dec 17 '12 at 20:10
-3

Doesn't ExecuteTypedList accomplish what you want to do? SubSonic will populate your DTO's/POCOs. From Rob Connery's blog:

ExecuteTypedList<> tries to match the names of the returned columns to the names of the properties of the passed-in type. In this example they match exactly - and that's not completely real world. You can get around this by aliasing the columns - in the same way you'd alias a SQL call:

return Northwind.DB.Select("ProductID as 'ID'", "ProductName as 'Name'", "UnitPrice as 'Price'")
            .From<Northwind.Product>().ExecuteTypedList<Product>();

Here's the link to Rob's Writing Decoupled, Testable code with SubSonic 2.1

David Robbins
  • 9,996
  • 7
  • 51
  • 82
  • `ExecuteTypedList<>` only exists in SubSonic 2, I 'm using 3. SS2 didn't even have lambdas... As an aside, your suggested solution would require that (a) I the codebase be revisited and some (but not all; sometimes we use LINQ to Objects on a result set we need in its entirety anyway) Func expressions be changed to Func, and (b) we give up any custom "mutating" logic and the option to have it. I 'm not just trying to get one statement to compile here. – Jon May 09 '10 at 16:20
  • Ok - I think I see. Essentially you'd like to write something like: DataContract.WidgetList().AsQueryable().Where(predicate) with the predicate of type DataContract but translated to ActiveRecord. – David Robbins May 09 '10 at 17:57
  • That sums it up perfectly, yes. – Jon May 09 '10 at 22:28
-4

I think Linq-To-Sql will produce the desirable SQL if you do your queries correctly. In this case, using IQueryable and deferred execution you can avoid returning all ActiveRecord.Widget records.

IEnumerable<DataContract.Widget> GetMany( 
    Func<DataContract.Widget, bool> predicate) 
{ 
    // get Widgets
    IQueryable<DataContract.Widget> qry = dc.Widgets.Select(w => TODO: CONVERT_TO_DataContract.Widget);

    return qry.Where(predicate);
}
bruno conde
  • 47,767
  • 15
  • 98
  • 117
  • Sorry, but this answer is both unhelpful and wrong. I 'm not sure what you wanted to express but the question is clearly all about expression trees, of which there is not even half in your code. – Jon May 09 '10 at 11:29
  • 1
    @Jon, I think your main objective is solving your problem !!! What I'm suggesting is a different aproach that doesn't use expression trees because it avoids the performnce problem from the begining. Please confirm the generated SQL in this case and you'll see that only the desired records are returned. – bruno conde May 09 '10 at 11:34
  • I still do not see what you are trying to suggest. Your code as I understand it says "Filter the queryable collection of DataContract.Widget with this predicate". Problem (1): There exists no `IQueryProvider` for `DataContract.Widget`. Problem (2): even if we wrote one, how would it be implemented? (a) by transforming the query expression and querying on `ActiveRecord.Widget`? [Problem (3): your code doesn't take a query expression but a lambda] This is what I 'm trying to do! or (b) by grabbing all records, converting them and filtering afterwards? This is what I 'm trying to avoid. – Jon May 09 '10 at 16:30
  • 1
    My code says: (1) take the `ActiveRecord.Widget` records and make a transformation (convert them to `DataContract.Widget`). (2) apply the desired filter to the resulting `IQueryable` from transformation. Although it seems you are performing 2 operations, in fact, Linq-to-SQL will use just one query to the database with the transformation and filtering. So, only the desired records are returned. – bruno conde May 09 '10 at 21:31
  • I would think that your first step will actually pull all records, but admittedly I don't know that for a fact. However, the other problem remains: I don't have an `IQueryable` implementation. – Jon May 09 '10 at 22:30