首页 文章

将谓词的表达式树变为目标另一种类型

提问于
浏览
49

简介

在我目前正在处理的应用程序中,每个业务对象有两种:“ActiveRecord”类和“DataContract”类 . 例如,会有:

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

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

数据库访问层负责在系列之间进行转换:您可以告诉它更新 DataContract.Widget ,它会神奇地创建一个具有相同属性值的 ActiveRecord.Widget 并保存它 .

尝试重构此数据库访问层时,问题浮出水面 .

问题

我想在数据库访问层中添加如下方法:

// Widget is DataContract.Widget

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

以上是一个简单的通用"get"方法,带有自定义谓词 . 唯一感兴趣的是我传递的是表达式树而不是lambda,因为在 IDbAccessLayer 中我正在查询 IQueryable<ActiveRecord.Widget> ;为了有效地做到这一点(想想LINQ to SQL)我需要传入一个表达式树,所以这个方法只是要求 .

障碍:参数需要从 Expression<Func<DataContract.Widget, bool>> 神奇地转换为 Expression<Func<ActiveRecord.Widget, bool>> .

尝试解决方案

我想在 GetMany 内做的是:

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
}

这不起作用,因为在典型情况下,例如:

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

...表达式树包含 MemberAccessExpression 实例,该实例具有 MemberInfo 类型的属性,用于描述 DataContract.Widget.Id . 表达式树及其参数集合( predicate.Parameters )中也有 ParameterExpression 个实例,它们描述了 DataContract.Widget ;所有这些都会导致错误,因为可查询主体不包含该类型的小部件,而是 ActiveRecord.Widget .

搜索了一下之后,我发现了System.Linq.Expressions.ExpressionVisitor(它的源代码可以在how-to的上下文中找到here),这提供了一种修改表达式树的便捷方法 . 在.NET 4中,此类包含在开箱即用中 .

有了这个,我实施了一个访客 . 这个简单的访问者只负责更改成员访问和参数表达式中的类型,但这足以使用谓词 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);
    }
}

有了这个访问者, GetMany 成为:

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.
}

结果

好消息是 lambda 构造得很好 . 坏消息是,当我尝试使用它时,它并没有爆炸,而且异常消息根本没有用 .

我检查了我的代码产生的lambda和一个具有相同表达式的硬编码lambda;他们看起来完全一样 . 我花了几个小时在调试器中试图找到一些区别,但我不能 .

当谓词是 w => w.Id == 0 时, lambda 看起来与 referenceLambda 完全相同 . 但是后者适用于例如 IQueryable<T>.Where ,而前者没有;我在调试器的即时窗口中尝试过这个 .

我还应该提一下,当谓词是 w => true 时,一切正常 . 因此,我假设我没有找到更多的线索 .

最终解决方案

考虑到问题的正确答案(下面两个;一个简短,一个带代码)问题解决了;我把代码和一些重要的注释放在separate answer中,以防止这个长问题变得更长 .

感谢大家的回答和评论!

6 回答

  • 5

    事实证明,棘手的部分只是新lambda的表达式树中存在的 ParameterExpression 实例必须 the same instances ,因为在 Expression.LambdaIEnumerable<ParameterExpression> 参数中传递 .

    请注意,在 TransformPredicateLambda 中我给 t => typeof(TNewTarget) 作为"type converter"函数;那是因为在这种特定情况下,我们可以假设所有参数和成员访问都属于那种特定类型 . 更高级的场景可能需要额外的逻辑 .

    代码:

    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();
            }
        }
    }
    
  • -2

    上面的Jon's own answer很棒,所以我将它扩展为处理方法调用,常量表达式等,以便现在它也适用于以下表达式:

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

    我也取消了 ExpressionTreeExplorer ,因为我们唯一需要的是ParameterExpressions .

    这是代码(更新:完成转换后清除缓存)

    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)));
        }
    }
    
  • 6

    我认为如果你正确地进行查询,Linq-To-Sql将生成所需的SQL . 在这种情况下,使用 IQueryable 和延迟执行可以避免返回所有 ActiveRecord.Widget 记录 .

    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);
    }
    
  • -4

    ExecuteTypedList不能完成你想做的事吗? SubSonic将填充您的DTO / POCO . 来自Rob Connery的博客:

    ExecuteTypedList <>尝试将返回列的名称与传入类型的属性名称相匹配 . 在这个例子中,它们完全匹配 - 这不是完全真实的世界 . 您可以通过对列进行别名来解决这个问题 - 就像您为SQL调用设置别名一样:

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

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

  • 15

    看来你在这里的VisitMember()中生成了两次参数表达式:

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

    ...因为base.Visit()最终会出现在我想象的VisitParameter中,并且在GetMany()本身中:

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

    如果您在正文中使用ParameterExpression,则必须如此与为Lambda声明的实例相同的实例(不仅仅是相同的类型和名称) . 我之前遇到过这种情况的问题,虽然我认为结果是我只是无法创建表达式,它只会引发异常 . 在任何情况下,您可能会尝试重用参数实例,看看它是否有帮助 .

  • 13

    我尝试了简单(不完整)的实现来改变表达式 p => p.Id == 15 (代码如下) . 有一个名为"CrossMapping"的类定义了原始类型和"new"类型与类型成员之间的映射 .

    对于每种表达式类型,有几个名为 Mutate_XY_Expression 的方法,它们会产生新的变异表达式 . 方法输入需要原始表达式( MemberExpression originalExpression )作为表达式的模型,列表或参数表达式( IList<ParameterExpression> parameterExpressions )是"parent"表达式定义的参数,应该由"parent's" body使用,映射对象( CrossMapping mapping )定义之间的映射类型和成员 .

    对于完整实现,您可能需要父项表达式而不是参数的更多信息 . 但模式应该是相同的 .

    正如您所知,示例并未实现访问者模式 - 这是因为简单性 . 但转换到它们没有障碍 .

    我希望,这会有所帮助 .

    代码(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;
            }
        }
    }
    

相关问题