C# 输出表达式值常量
我必须通过http将表达式发送到后端。此后端知道enum Fun,但没有funs的参考 我的工作是以后端仍然可以反序列化的方式序列化exp2 有没有办法强制传递枚举值而不是数组元素的引用C# 输出表达式值常量,c#,serialization,expression,func,C#,Serialization,Expression,Func,我必须通过http将表达式发送到后端。此后端知道enum Fun,但没有funs的参考 我的工作是以后端仍然可以反序列化的方式序列化exp2 有没有办法强制传递枚举值而不是数组元素的引用 var funs = new[] { Fun.Low, Fun.High }; Expression<Func<Funky, bool>> exp1 = x => x.Status == Fun.Low; Expression<Func<Funky, bool>&
var funs = new[] { Fun.Low, Fun.High };
Expression<Func<Funky, bool>> exp1 = x => x.Status == Fun.Low;
Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0];
Console.WriteLine(exp1);
//Expected: x => (Convert(x.Status, Int32) == 1)
Console.WriteLine(exp2);
//Actual output: x => (Convert(x.Status, Int32) == Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32))
public enum Fun : int {
Low = 1,
Middle = 2,
High = 420
}
public class Funky {
public Fun Status {get;set;} = Fun.High;
}
现实生活中的例子:
后端拥有一个将通过EF-LINQ查询的数据库。
前端应该向后端发送准确的LINQ查询
假设前端的用户有一个清单,通过该清单,他可以切换他可以从后端查询的时髦对象:
[x] 低
[x] 中间的
高
->输出var funs=new[]{Fun.Low,Fun.Middle};
现在前端必须将表达式组合在一起,如下所示:
表达式exp2=x=>x.Status==funs[0]| | x.Status==funs[1]
并在将其发送到后端之前对其进行序列化。
后端无法理解funs[0]或funs[1]。但是后端知道枚举的乐趣,可以正确地反序列化1和2。基本上,您需要重写表达式以删除所有间接寻址,并直接使用文本值。这可以通过ExpressionVisitor实现—下面显示了一个简化的示例,它可以处理您的场景—但如果您想处理更复杂的事情,如本地评估的方法调用,则需要添加更多覆盖方法: 公共类简化访问者:ExpressionVisitor { 受保护的重写表达式VisitBinaryBinaryExpression节点 { 如果node.NodeType==ExpressionType.ArrayIndex { 如果Visitnode.Left是ConstantPression Left &&左。值为数组arr&&arr.Rank==1 &&Visitnode.右边是恒久不变的Pression,对吗 { var type=left.type.GetElementType; 右转。数值 { 案例一: 返回表达式.Constantarr.GetValuei,类型; 案例长l: 返回表达式.Constantarr.GetValuel,类型; } } } 返回base.VisitBinarynode; } 受保护的重写表达式VisitUnaryExpression节点 { 如果node.NodeType==ExpressionType.Convert &&Visitnode。操作数为常量表达式arg { 尝试 { 返回表达式.常量 Convert.ChangeTypearg.Value,node.Type,node.Type; } 抓住{}//尽最大努力 } 返回base.VisitUnarynode; } 受保护的重写表达式VisitMemberExpression节点 { 如果node.NodeType==ExpressionType.MemberAccess&&Visitnode.Expression为ConstantPression目标 { 交换节点.成员 { 案例属性信息属性: 返回表达式.Constantproperty.GetValuetarget.Value,property.PropertyType; 案例字段信息字段: 返回表达式.Constantfield.GetValuetarget.Value,field.FieldType; } } 返回base.VisitMembernode; } } 用法: var visitor=新简化访客; exp2=Expressionvisitor.Visitexp2;
我不明白,您将exp1声明为Func,您传递一个Funky对象,它返回一个布尔值。但是,当您向控制台写入内容时,您并没有调用该函数,您只是在说:嘿,这个表达式是什么。在我看来,你想调用实际的Func,比如exp1afuno,先生,这不是调用表达式。这些表达式的工作原理相同。但将它们转换为字符串会产生不同的结果。由于exp2输出不能被没有引用funs数组的人正确解释,您可以详细说明为什么在后端不知道如何索引时要向后端发送索引器表达式吗?也许是一个更现实的例子?您可以在表达式本身中包含funs的定义,但这对我来说没有多大意义,因为exp1和exp2基本上可以做相同的事情。@JessedeWit我添加了一个真实的示例。我相信这样发送LINQ是一种不好的方式,但我必须这样做,我无法更改后端尝试发送表达式exp2=x=>x.Status==Fun.Low | | x.Status==Fun.High;相反,完美无瑕!这正是我所要求的。非常感谢,我现在还可以很好地扩展这些访问者方法,添加不相等/更高/更低/等等。
//another tests
var afun = new Funky();
var param = Expression.Parameter(typeof(Funky), "x");
var key = afun.GetType().GetProperty("Status");
var lhs = Expression.MakeMemberAccess(param, key);
var rhs = Expression.ArrayIndex(Expression.Constant(funs), Expression.Constant(0));
var body = Expression.Equal(lhs, rhs);
var exp3 = Expression.Lambda<Func<Funky, bool>>(body, param);
Console.WriteLine(exp3);
//x => (x.Status == value(Fun[])[0])