动态委托给最小的 api

2024-02-21

各位程序员大家好。 基本上,我想将动态构建的委托传递给最小的 api MapGet 或 MapPost 方法。这是构造委托的方法:

private static Delegate GetDelegate(Type type, MethodInfo method, ParameterInfo[] parameters)
{
    /* Method dynamically build this lambda expression:
    * (Type1 arg1, Type2 arg2, ..., TypeN argN) =>
    {
        var instance = GetTypeInstance(type);
        return instance.SomeMethod(arg1, arg2, ..., argN);
    }
    * Where N = number of arguments
    */

    var paramExpresions = new List<ParameterExpression>();
    foreach (var parameter in parameters)
          paramExpresions.Add(Expression.Parameter(parameter.ParameterType, parameter.Name));

    // Instance variable
    var instance = Expression.Variable(type, "instance");

    // Get instance of type
    MethodInfo getTypeInstance = typeof(DynamicControllerCompiler).GetMethod("GetTypeInstance");
    var callExpression = Expression.Call(getTypeInstance, Expression.Constant(type));
    var expressionConversion = Expression.Convert(callExpression, type);
    var assignSentence = Expression.Assign(instance, expressionConversion);

    var returnTarget = Expression.Label(method.ReturnType);
    var returnExpression = Expression.Return(returnTarget, Expression.Call(instance, method, paramExpresions), method.ReturnType);
    var returnLabel = Expression.Label(returnTarget, Expression.Default(method.ReturnType));

    var fullBlock = Expression.Block(
        new[] { instance },
        assignSentence,
        returnExpression,
        returnLabel
    );

    var lambda = Expression.Lambda(fullBlock, "testLambda", paramExpresions);
    return lambda.Compile();
}

引用的方法“GetTypeInstance”只是从容器返回服务,但为了简单起见,让它这样:

public static object GetTypeInstance(Type type)
{
    return new EchoService();
}

服务非常简单:

public class EchoService
{
    public string Echo(string message)
    {
        return message;
    }

    public string EchoDouble(string message)
    {
        return message + "_" + message;
    }
}

所以我想使用它将 get 方法映射到最小 api,如下所示:

var type = typeof(EchoService);
foreach (var method in type.GetMethods())
{
    ParameterInfo[] parameters = method.GetParameters();
    var methodDelegate = GetDelegate(type, method, parameters);

    //test
    var result = methodDelegate.DynamicInvoke("test");

    app.MapGet($"api/{method.Name}", methodDelegate);
}

为了测试动态委托是否有效,我用“DynamicInvoke”调用它,一切看起来都很好。但是,如果我将委托传递给 MapGet,则会引发错误:

系统.InvalidOperationException:'参数没有名称!是生成的吗?所有参数都必须命名。

我似乎无法理解发生了什么事。如果由 DynamicInvoke 调用,委托可以正常工作,并且所有参数内部都有名称。


问题的根源在于表达式树编译的工作原理。我没有足够的知识来解释原因,但默认情况下它不会发出参数名称:

Expression<Func<int, string>> expr = i => i.ToString();
var compiledMethod = expr.Compile().Method;
Console.WriteLine(compiledMethod.GetParameters().Count(p => p.Name != null)); // prints "0"

您可以尝试使用以下方法来克服这个问题Compile https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.lambdaexpression.compile?view=net-6.0#System_Linq_Expressions_LambdaExpression_Compile_System_Boolean_过载接受bool preferInterpretation参数(至少它适用于我的设置),但随后您将面临另一个问题 - 编译方法实际上有两个参数(我认为一个用于处理闭包):

compiledMethod = expr.Compile(true).Method;
Console.WriteLine(compiledMethod.GetParameters().Count(p => p.Name != null)); // prints "2"

我从这里看不到简单的解决方法。

您可以尝试的其他方法 - 使用动态代码生成System.Reflection.Emit名称空间 https://learn.microsoft.com/en-us/dotnet/framework/reflection-and-codedom/emitting-dynamic-methods-and-assemblies or Roslyn https://learn.microsoft.com/en-us/archive/msdn-magazine/2017/may/net-core-cross-platform-code-generation-with-roslyn-and-net-core#executing-code-the-emit-apis但我想说,更好的方法是在构建时使用以下命令生成此代码源发生器 https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview.

在某些情况下还有另一种值得考虑的方法 - 创建一个具有所需设置的通用方法Map...并像完成一样通过反射来调用它在这个答案中 https://stackoverflow.com/a/75932445/2501279.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

动态委托给最小的 api 的相关文章

随机推荐