代码之家  ›  专栏  ›  技术社区  ›  Jon

将谓词的表达式树变异为目标类型

  •  50
  • Jon  · 技术社区  · 14 年前

    简介

    在我目前正在开发的应用程序中,每个业务对象有两种类型:“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到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 . 还有 ParameterExpression 表达式树及其参数集合中的实例( predicate.Parameters )描述 datacontract.widget程序 ;所有这些都将导致错误,因为可查询的主体不包含该类型的小部件,而是 activerecord.widget程序 .

    经过一番搜寻,我发现 System.Linq.Expressions.ExpressionVisitor (可以找到其来源 here 在how-to的上下文中),它提供了一种修改表达式树的方便方法。在.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);
        }
    }
    

    有了这个客人, 得到许多 变成:

    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 , 兰姆达 看起来很像 referenceLambda . 但后者与e.g. IQueryable<T>.Where ,而前者没有;我已经在调试器的即时窗口中尝试过。

    我还应该提到,当谓词是 w => true ,一切正常。因此,我认为我在访客中做的工作还不够,但我找不到更多的线索。

    最终解决方案

    在考虑了问题的正确答案(下面两个;一个简短,一个带有代码)之后,问题就解决了;我把代码和一些重要的注释放在 separate answer 为了不让这个长问题变得更长。

    感谢大家的回答和评论!

    6 回复  |  直到 14 年前
        1
  •  15
  •   Diego Veralli    14 年前

    您似乎在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声明的实例相同(而不仅仅是相同的类型和名称)。 我以前遇到过这种情况,虽然我认为结果是我无法创建表达式,但它会抛出一个异常。在任何情况下,您都可以尝试重用参数实例,看看它是否有帮助。

        2
  •  13
  •   Jon    12 年前

    结果发现,最棘手的部分就是 ParameterExpression 新lambda的表达式树中存在的实例必须是 相同的实例 就像在 IEnumerable<ParameterExpression> 参数 Expression.Lambda .

    注意里面 TransformPredicateLambda 我在给 t => typeof(TNewTarget) 作为“类型转换器”函数;这是因为在这个特定的情况下,我们可以假设所有参数和成员访问都属于那个特定的类型。更高级的场景可能需要额外的逻辑。

    代码:

    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();
            }
        }
    }
    
        3
  •  6
  •   TcKs    14 年前

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

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

    对于完整的实现,您可能需要从父表达式获得比参数更多的信息。但模式应该是一样的。

    sample并没有实现visitor模式,正如您所知——这是因为它很简单。但是没有任何障碍可以转化为他们。

    我希望,这会有帮助。

    代码(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;
            }
        }
    }
    
        4
  •  5
  •   Community Ian Goodfellow    7 年前

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

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

    我也放弃了 ExpressionTreeExplorer 因为我们只需要参数表达式。

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

    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)));
        }
    }
    
        5
  •  -2
  •   David Robbins    14 年前

    executeTypedList没有完成您想要的任务吗?亚音速将填充你的DTO/POCO。来自Rob Connery的博客:

    ExecuteTypedList<gt;尝试匹配 返回到的列的名称 物业名称 传入类型。在这个例子中 完全匹配-但那不是 完全真实的世界。你可以得到 通过对列进行别名处理- 以同样的方式将sql命名为 呼叫:

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

    这是Rob的链接 Writing Decoupled, Testable code with SubSonic 2.1

        6
  •  -4
  •   bruno conde    14 年前

    我认为如果正确地执行查询,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);
    }