c# - 输出表达式值类似常数

标签 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>> 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;
}

问题:如何使 exp2 的结果与 exp1 相同?

_____________________________________________

背景信息:

exp1将枚举值序列化为 1后端可以正确解释。

exp2序列号funs[0]作为对实际数组元素的引用,如下所示:Convert(value(Program+<>c__DisplayClass0_0).funs[0], Int32)

我也尝试过exp3但这输出的值仍然作为引用而不是常量枚举值。

到目前为止我已经尝试过:

//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])

现实生活中的例子:

后端拥有一个将通过 EF-LINQ 查询的数据库。 前端应该将准确的 LINQ 查询发送到后端。

假设前端用户有一个 list ,通过该 list 他可以切换他可以从后端查询哪些 Funky 对象: [x] 低 [x] 中 [_]高

-> 输出 var funs = new[] { Fun.Low, Fun.Middle }; 现在前端必须将表达式组合在一起,如下所示: Expression<Func<Funky, bool>> exp2 = x => x.Status == funs[0] || x.Status == funs[1];

并在将其发送到后端之前对其进行序列化。 后端无法理解funs[0]funs[1] 。但后端知道 enum Fun并且可以反序列化12正确。

最佳答案

基本上,您需要重写表达式以删除所有间接寻址并直接使用文字值。这可以通过 ExpressionVisitor 来完成 - 下面显示了一个简化的示例(它处理您的场景) - 但如果您想处理更复杂的事情,例如方法调用(本地评估),您需要添加更多覆盖方法:

    public class SimplifyingVisitor : ExpressionVisitor
    {
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node.NodeType == ExpressionType.ArrayIndex)
            {
                if (Visit(node.Left) is ConstantExpression left
                    && left.Value is Array arr && arr.Rank == 1
                    && Visit(node.Right) is ConstantExpression right)
                {
                    var type = left.Type.GetElementType();
                    switch (right.Value)
                    {
                        case int i:
                            return Expression.Constant(arr.GetValue(i), type);
                        case long l:
                            return Expression.Constant(arr.GetValue(l), type);
                    }
                }
            }
            return base.VisitBinary(node);
        }
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Convert
                 && Visit(node.Operand) is ConstantExpression arg)
            {
                try
                {
                    return Expression.Constant(
                        Convert.ChangeType(arg.Value, node.Type), node.Type);
                }
                catch { } //best efforts
            }
            return base.VisitUnary(node);
        }
        protected override Expression VisitMember(MemberExpression node)
        {
            if (node.NodeType == ExpressionType.MemberAccess && Visit(node.Expression) is ConstantExpression target)
            {
                switch (node.Member)
                {
                    case PropertyInfo property:
                        return Expression.Constant(property.GetValue(target.Value), property.PropertyType);
                    case FieldInfo field:
                        return Expression.Constant(field.GetValue(target.Value), field.FieldType);
                }
            }
            return base.VisitMember(node);
        }
    }

用法:

var visitor = new SimplifyingVisitor();
exp2 = (Expression<Func<Funky, bool>>)visitor.Visit(exp2);

关于c# - 输出表达式值类似常数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66637240/

相关文章:

c# - 处理未定义抽象方法C#的调用

c# - 如何使用 JSON.NET 将 JSON 数组中的对象名称用作属性值?

java - Proguard 和 lambda 表达式

c - 算术 double 表达式和 c/c++

c - 为什么在 C 中 const 对象不是编译时常量表达式?

c# - 交换全局 XAML 样式/主题

c# - 使用 C# CryptoStream 的 Java 等价物加密和解密字符串

c# - 提供程序 'SqlServer-20' 没有元数据信息

c# - Xamarin Studio Android 应用程序 "aapt.exe"退出,代码为 -1073741819 错误 MSB6006 :

json - 将集合序列化和反序列化为 JSON Delphi