转换表达式树
- 作者: 纵之影杯酒难续
- 来源: 51数据库
- 2022-12-15
问题描述
要有:
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; } }
- C#通过fleck实现wss协议的WebSocket多人Web实时聊天(附源码)
- 团队城市未满足要求:MSBuildTools12.0_x86_Path 存在
- 使用 MSBuild.exe 在发布模式下构建 C# 解决方案
- 当我发布 Web 应用程序时,AfterPublish 脚本不运行
- 构建时 T4 转换的产品仅在下一个构建中使用
- ASP.NET Core Application (.NET Framework) for Windows x64 only error in project.assets.json
- 新的 .csproj 格式 - 如何将整个目录指定为“链接文件"到子目录?
- 如何将条件编译符号(DefineConstants)传递给 msbuild
- MSBuild 支持 Visual Studio 2017 RTM 中的 T4 模板
- NuGet 包还原找不到包,没有源
