【问题标题】:Calling MethodInfo.Invoke Fails in Azure Function StartupAzure 函数启动中调用 MethodInfo.Invoke 失败
【发布时间】:2020-08-28 18:23:38
【问题描述】:

我有一个 .Net Core 3.1 应用程序,它被实现为启动时失败的 Azure 函数 (v3)。在函数应用项目中,我实现了一个Startup 类,它将各种服务添加到IServiceCollection 集合中以进行依赖注入。我有一个需要使用反射手动加载的程序集,并调用一种方法,该方法将该程序集中定义的所有服务添加到IServiceCollection 集合中。正如您在下面看到的,失败的是行 method.Invoke(null, new Object[] { services });。加载程序集、获取类型信息或方法信息似乎没有问题,因为此代码中没有引发任何异常。您可以看到我正在调用静态类的静态方法,因此将null 参数传递给Invoke 方法。我尝试使用非静态类并使用Activator.CreateInstance 方法将对象实例传递给Invoke 方法,但得到相同的错误。这段代码在本地运行时可以正常执行,只有发布到 Azure Function 资源后才会出现问题。

这是我的 Startup.cs 文件的内容:

using System;
using System.IO;
using System.Reflection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Some.Name.Space.FunctionApp.Startup))]
namespace Some.Name.Space.FunctionApp
{
    public sealed class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            IServiceCollection services = builder.Services;

            var assemblyNamespace = "Some.Name.Space";
            String? baseDirectory = Path.GetDirectoryName(GetType().Assembly.Location);
            if (!String.IsNullOrEmpty(baseDirectory))
            {
                Assembly assembly = Assembly.Load(File.ReadAllBytes(Path.Combine(baseDirectory, $"{assemblyNamespace}.dll")));
                var typeName = $"{assemblyNamespace}.SomeClassName";
                Type? type = assembly.GetType(typeName);
                if (type != null)
                {
                    var methodName = "SomeMethodName";
                    MethodInfo? method = type.GetMethod(methodName);
                    if (method != null)
                    {
                        method.Invoke(null, new Object[] { services });  // THIS IS LINE THAT FAILS.
                    }
                    else
                    {
                        throw new MissingMethodException($"Unable to find the {methodName} method in the {typeName} type.");
                    }
                }
                else
                {
                    throw new TypeLoadException($"Unable to load {typeName} from {assembly.GetName()}.");
                }
            }
            else
            {
                throw new DirectoryNotFoundException("Unable to get the Location directory for the function application.");
            }
        }
    }
}

我知道下面的错误消息没有准确描述问题,因为如果我简单地注释掉失败的一行,整个应用程序运行良好,并且还有很多其他代码依赖于“Microsoft. Extensions.DependencyInjection.Abstractions 的程序集。这是我在 Azure 中看到的错误的屏幕截图:

【问题讨论】:

  • 您在该例程中动态加载的库需要Microsoft.Extensions.DependencyInjection.Abstractions 命名空间。因此,请确保打包此 nuget 包:nuget.org/packages/… 如果您的项目已导入该包,请将您正在加载的库与您的 Azure 函数 dll 放在同一目录中。
  • 我动态加载的库确实使用了Microsoft.Extensions.DependencyInjection 命名空间。该库通过使用的单独包间接引用了“Microsoft.Extensions.DependencyInjection.Abstractions.dll”(该单独包引用了 *.Abstractions.dll 包)。
  • 另外,如果我删除反射功能,并设置对包的项目/程序集引用并直接调用该方法(它作为IServiceCollection接口的扩展方法实现),那么一切正常.在我的情况下,我不需要有对我试图动态加载的这个库的项目/程序集引用。

标签: c# .net-core azure-functions system.reflection methodinfo


【解决方案1】:

我不能老实说为什么我的解决方案有效,但我怀疑这与 Assembly.Load(AssemblyName assemblyRef) 方法可能如何使用 System.Runtime.Loader.AssemblyLoadContext 类有关。我怀疑我的原始实现没有加载正确的程序集,即使它能够加载具有我期望的名称的程序集。下面的解决方案具有默认的 VersionCulturePublicKeyToken 值用于显示,因此您需要在实现中为这些属性使用正确的值。

以下是新 Startup.cs 文件的内容:

using System;
using System.Reflection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(Some.Name.Space.FunctionApp.Startup))]
namespace Some.Name.Space.FunctionApp
{
    public sealed class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            IServiceCollection services = builder.Services;
            var assemblyNamespace = "Some.Name.Space";
            AssemblyName assemblyName = new AssemblyName($"{assemblyNamespace}, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
            Assembly assembly = Assembly.Load(assemblyName);
            var typeName = $"{assemblyNamespace}.SomeType";
            Type? type = assembly.GetType(typeName);
            if (type != null)
            {
                var methodName = "SomeMethod";
                MethodInfo? method = type.GetMethod(methodName);
                if (method != null)
                {
                    method.Invoke(null, new Object[] { services });
                }
                else
                {
                    throw new MissingMethodException($"Unable to find the {methodName} method in the {typeName} type.");
                }
            }
            else
            {
                throw new TypeLoadException($"Unable to load {typeName} from {assembly.GetName()}.");
            }
        }
    }
}

【讨论】:

    猜你喜欢
    • 2014-01-23
    • 2014-11-05
    • 2022-12-15
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    相关资源
    最近更新 更多