将Action转换为Lambda表达式

3
我正在使用NRules,尝试从数据库加载规则。为此,我必须使用反射来生成表达式。
public class Product
{
     public string Attribute1 { get; }
     public List<int> Category { get; set; }   
     public void AddCategory (int category){
          this.Category.Add(category);
     }
}

using NRules.RuleModel;
using NRules.RuleModel.Builders;

var builder = new RuleBuilder();
//some logic for buildin lefthand side
Expression<Action<IContext, Product>> action = (ctx, product) => product.AddCategory(25);
builder.RightHandSide().Action(action);

我的目标是在运行时生成"Expression<Action<IContext, Product>> action = (ctx, product) => product.AddCategory(25);"。我认为使用反射是实现这一目标的唯一方法,因为我需要从数据库中读取某些值。
我可以通过使用反射来生成Action:
Type actionType = typeof(Action<>).MakeGenericType(new Type[] { typeof(IContext),      
Type.GetType(actualModelName) });
MethodInfo eventMethodInfo = type.GetMethod("AddCategory");
Action actionFromReflection  = (Action)Delegate.CreateDelegate(actionType, eventMethodInfo);

但是NRules方法需要以LambdaExpression作为参数。

我该如何将“actionFromReflection”转换为LambdaExpression?

LambdaExpression le = actionFromReflection  ???

我建议您不要使用反射,而是拥有一组常见命令,这些命令由数据库中的规则驱动,但不是动态生成的。我猜想您的产品设计师认为“基于数据库的规则意味着我们可以生成规则而无需任何开发人员的输入”,但实际上,这种情况从未发生过。 - Neil
嗨Neil,感谢您的反馈。实际上,动态生成规则是我们(开发人员)的想法。我们的规则非常简单。 - fkucuk
2个回答

2

委托是指实际编译的代码,而Lambda表达式是一个表达式树,接近源代码,只不过不是以文本形式呈现。您可以调用您的委托,但不能做其他事情。从IL代码创建源代码将是反汇编器的工作。

使用反射意味着“使用已经编译的东西”,这与在运行时创建它相反。因此,这是错误的方法。

要在运行时创建LambdaExpression,您需要执行以下操作:

        ParameterExpression ctx = Expression.Parameter(typeof(Context), "ctx");
        ParameterExpression product = Expression.Parameter(typeof(Product), "product");

        LambdaExpression lambdaExpr = Expression.Lambda(
            /* your actual code, depending on what you want to do */
                Expression.Add(
                ctx,
                Expression.Constant(1)
            ),
            new List<ParameterExpression>() { ctx, product })

实际上,这个示例构建了ctx => ctx +1。 如果没有返回值,则可能有一个Expression.Call。 您需要进一步调查如何将您想要的表达为表达式树。这是一个广泛的话题。

LambdaExpression只需进行强制转换即可:

var expr = (Expression<Action<ctx, product>>) lambdaExpr;

0

在阅读 Holger 的回答并苦苦挣扎了几个小时后,我想出了以下解决方案:

为了在运行时创建如下表达式:

Expression<Action<IContext, Model>> printExpression = (ctx, model) => model.AddCategory(5);

我已经编写了一个静态方法,用于生成满足我的需求的操作:
public static Expression<Action<T, K>> GetActionExpression<T, K>(string methodName, int val)
{
    ParameterExpression ctx = Expression.Parameter(typeof(T), "ctx");
    ParameterExpression product = Expression.Parameter(typeof(K), "model");
    ParameterExpression actionMethodParam = Expression.Parameter(typeof(int), "val");

    var lambdaExpr = Expression.Lambda<Action<T, K>>(
        Expression.Call(
                  product,
                  typeof(K).GetMethod(methodName, new Type[] { typeof(int) }),
                  Expression.Constant(val)), new ParameterExpression[] {
                  ctx, product
              }
        );
        return lambdaExpr;
}

这个方法会生成如下表达式:

(ctx, model) => model.{methodName}(val);

然后我使用反射调用了这个方法:

foreach(var item in actions) {

var actionMethodInfo = typeof(ExpressionBuilder).GetMethod("GetActionExpression")
                        .MakeGenericMethod(typeof(IContext), type);

                    LambdaExpression action = (LambdaExpression)actionMethodInfo.Invoke(null, new object[] {item.MethodName, item.Value });

}

就是这样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接