问题描述
要有:
Expression>exp1 = x =>x.mesID == 1;表达式<Func<MessageDTO,bool>>exp2 = x =>x.mesID == 1;
现在我需要将 exp1 传递给 _db.Messages.where(exp1);
问题是我只有 exp2,我需要将类型转换为 Message ,所有属性都相同!>
现在我这样做:
var par = Expression.Parameter(typeof(Message));var ex = (Expression)Expression.Lambda(exp2.Body, par);
问题是输入参数被改变了是的!但是 lambda "x.mesID" 主体内的 x 是旧类型.
有什么方法可以改变正文中的所有参数类型,或者改变输入参数,让它也反映正文?
我想这是我在使用 LINQ 时总是遇到的一个大问题,因为在层之间我不能传递生成的类,因为这会使层耦合,所以我必须制作轻量级的类,现在我如何使用类似的方法_db.Messages.where();从繁忙层?!业务层对 Message 类型一无所知,它只知道 MessageDTO.
不,基本上.表达式树是不可变的,并且包含完整的成员元数据(即 mesID
是 messageDTO.mesID
).为此,您必须从头开始(通过访问者)重建表达式树,处理您需要支持的每个节点类型.
如果表达式树是基本这应该没问题,但是如果你需要支持整个色域呢?巨大的 PITA(尤其是在 .NET 4 中,它添加了更多节点类型).
<小时>一个基本示例,它只是满足示例的要求;您需要为更复杂的表达式添加更多节点类型:
使用系统;使用 System.Collections.Generic;使用 System.Linq;使用 System.Linq.Expressions;静态类程序{静态无效主(){表达式<Func<Message,bool>>exp1 = x =>x.mesID == 1;var exp2 = Convert(exp1);}静态表达式<Func<TTo, bool>>Convert(Expression> expr){字典<表达式,表达式>substitutues = new Dictionary();var oldParam = expr.Parameters[0];var newParam = Expression.Parameter(typeof(TTo), oldParam.Name);substitutues.Add(oldParam, newParam);表达式 body = ConvertNode(expr.Body, substitutues);return Expression.Lambda(body, newParam);}static Expression ConvertNode(Expression node, IDictionary subst){如果(节点==空)返回空;if (subst.ContainsKey(node)) 返回 subst[node];开关(节点.节点类型){case ExpressionType.Constant:返回节点;case ExpressionType.MemberAccess:{var me = (MemberExpression)node;var newNode = ConvertNode(me.Expression, subst);return Expression.MakeMemberAccess(newNode, newNode.Type.GetMember(me.Member.Name).Single());}case ExpressionType.Equal:/* 可能适用于一系列常见的二进制表达式 */{var be = (BinaryExpression)node;return Expression.MakeBinary(be.NodeType, ConvertNode(be.Left, subst), ConvertNode(be.Right, subst), be.IsLiftedToNull, be.Method);}默认:抛出新的 NotSupportedException(node.NodeType.ToString());}}}类消息 { 公共 int mesID { 获取;放;} }类 MessageDTO { public int mesID { get;放;} }
let there be :
Expression<Func<Message, bool>> exp1 = x => x.mesID == 1;
Expression<Func<MessageDTO, bool>> exp2 = x => x.mesID == 1;
now i need to pass exp1 to _db.Messages.where(exp1);
problem is i only have exp2, i need to convert the type to Message , All properties are the same !
now i do this :
var par = Expression.Parameter(typeof(Message));
var ex = (Expression<Func<Message, bool>>)Expression.Lambda(exp2.Body, par);
problem with this is the input paramter gets changed yes ! but the x inside the body of the lambda "x.mesID" is of the old type.
any way to change all the parameters type in the body too or change the input parameter in away it reflect the body too ?
i guess this is a big problem i always have with LINQ , since between layers i cant pass the generated classes , as this will make layers coupled , so i have to make light weight classes , now how do i use a method like _db.Messages.where(); from busiess layer ?!! while busniess layer doesnt know anything about Message type it only know MessageDTO.
No, basically. Expression trees are immutable, and contain full member meta-data (i.e. that mesID
is messageDTO.mesID
). To do this, you would have to rebuild the expression tree from scratch (via a visitor), handling every node type you need to support.
If the expression tree is basic this should be OK, but if you need to support the entire gamut? a huge PITA (especially in .NET 4, which adds a lot more node-types).
A basic example that does just what is required for the example; you would need to add more node-types for more complex expressions:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
static class Program
{
static void Main()
{
Expression<Func<Message, bool>> exp1 = x => x.mesID == 1;
var exp2 = Convert<Message, MessageDTO>(exp1);
}
static Expression<Func<TTo, bool>> Convert<TFrom, TTo>(Expression<Func<TFrom, bool>> expr)
{
Dictionary<Expression,Expression> substitutues = new Dictionary<Expression,Expression>();
var oldParam = expr.Parameters[0];
var newParam = Expression.Parameter(typeof(TTo), oldParam.Name);
substitutues.Add(oldParam, newParam);
Expression body = ConvertNode(expr.Body, substitutues);
return Expression.Lambda<Func<TTo,bool>>(body, newParam);
}
static Expression ConvertNode(Expression node, IDictionary<Expression, Expression> subst)
{
if (node == null) return null;
if (subst.ContainsKey(node)) return subst[node];
switch (node.NodeType)
{
case ExpressionType.Constant:
return node;
case ExpressionType.MemberAccess:
{
var me = (MemberExpression)node;
var newNode = ConvertNode(me.Expression, subst);
return Expression.MakeMemberAccess(newNode, newNode.Type.GetMember(me.Member.Name).Single());
}
case ExpressionType.Equal: /* will probably work for a range of common binary-expressions */
{
var be = (BinaryExpression)node;
return Expression.MakeBinary(be.NodeType, ConvertNode(be.Left, subst), ConvertNode(be.Right, subst), be.IsLiftedToNull, be.Method);
}
default:
throw new NotSupportedException(node.NodeType.ToString());
}
}
}
class Message { public int mesID { get; set; } }
class MessageDTO { public int mesID { get; set; } }
这篇关于转换表达式树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!