azure - .net 7 隔离的 Azure 函数依赖注入(inject)在部署时失败(在本地工作)

标签 azure asp.net-core dependency-injection azure-functions

编辑::已使用显示实际服务的扩展方法更新了代码片段

我有一个Web api项目和一个独立的函数项目。 两者几乎都使用相同的 DI,以至于我为 DI 加载提取了一些扩展方法。

我的函数是由服务总线消息触发的。如果我在本地调试并向总线发送消息,我可以 100% 处理它。如果我部署该函数然后向总线发送消息,那么它会因 DependencyInjection 错误而终止,无法实例化我的类。

这是我的消息监听函数。

    public class ProcessingAnalyseDocumentsConsumer {

    private readonly ILogger<ProcessingAnalyseDocumentsConsumer> _logger;
    private readonly FormRecogniserService _formRecogniserService;
    private readonly ReprocessEncounterService _reprocessEncounterService;
    private readonly DataContext _context;

    public ProcessingAnalyseDocumentsConsumer(
        ILogger<ProcessingAnalyseDocumentsConsumer> logger,
        FormRecogniserService formRecogniserService,
        ReprocessEncounterService reprocessEncounterService,
        DataContext context) {
        _logger = logger;
        _formRecogniserService = formRecogniserService;
        _reprocessEncounterService = reprocessEncounterService;
        _context = context;
    }

    [Function("ProcessingAnalyseDocumentsConsumer")]
    public async Task RunAsync(
        [ServiceBusTrigger(Constants.QUEUES_PROCESSING_ANALYSIS_DOCUMENTS, Connection = "ServiceBusConnection")]
        ReprocessSingleDoc message) {
        
        _logger.LogInformation($"C# ServiceBus queue trigger function processed message: {JsonConvert.SerializeObject(message)}");

        var item = _context.LocationCities.FirstOrDefault();
        _logger.LogInformation($"Found item in db: {JsonConvert.SerializeObject(item)}");

    }
}

我的函数的 Program.cs 如下所示

    var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(async (hostContext, services) => {
        var configuration = hostContext.Configuration;
        var connectionString = configuration["DefaultConnection"];
        var env = hostContext.HostingEnvironment;

        services.AddDbContext<DataContext>(dbContextOptions =>
            dbContextOptions
            .UseLazyLoadingProxies()
            .UseSqlServer(
                connectionString,
                b => b.MigrationsAssembly("WEBAPIPROJECTASSEMBLY"))
            );

        if (!env.IsDevelopment()) {

            services.AddAzureClients(azureClientFactoryBuilder => {
                azureClientFactoryBuilder.AddSecretClient(configuration.GetSection("KeyVault"));
            });

            Console.WriteLine("adding azure vault");
            services.AddSingleton<IKeyVaultService, KeyVaultService>();
        }
        else {
            Console.WriteLine("adding debug vault");
            services.AddSingleton<IKeyVaultService, DebugKeyVaultService>();
        }

        await services.AddSensitiveServicesAsync(); //extension methods to add services
        services.AddHttpContextAccessor(); //make http context available
        services.ServiceDependencies(); //extension methods to add services
    });

await host.Build().RunAsync();

以下是正在调用的扩展方法。

    public static async Task<IServiceCollection> AddSensitiveServicesAsync(this IServiceCollection services) {

    var serviceProvider = services.BuildServiceProvider();
    var keyVault = serviceProvider.GetService<IKeyVaultService>();

    if (keyVault == null) {
        throw new NullReferenceException(nameof(keyVault));
    }

    await services.AddBlobStorage(keyVault);
    await services.AddFormRecogniserService(keyVault);
    return services;
}

public static async Task<IServiceCollection> AddBlobStorage(
    this IServiceCollection services,
    IKeyVaultService keyVault) {

    var blobStorageConnectionString = await keyVault.GetSecret("blob-storage-connection");
    services.AddTransient(t => new BlobStorageService(blobStorageConnectionString));
    return services;
}

public static async Task<IServiceCollection> AddFormRecogniserService(
    this IServiceCollection services,
    IKeyVaultService keyVault) {

    var secret_key = await keyVault.GetSecret("form-recogniser-key");
    var secret_endpoint = await keyVault.GetSecret("form-recogniser-endpoint");

    var serviceProvider = services.BuildServiceProvider();
    var blobStorage = serviceProvider.GetService<BlobStorageService>();

    if (blobStorage == null) {
        throw new NullReferenceException(nameof(blobStorage));
    }

    services.AddTransient(t => new FormRecogniserService(secret_key, secret_endpoint, blobStorage));
    return services;
}

唯一复杂的事情是我创建了一个用于调试的调试 keystore 并进行了环境切换 - 但这在本地有效,当我删除它时 - 一切仍然中断。我的 blob 存储服务和表单识别器服务也是使用从 keyvault 检索到的 secret 构建的,因此它们是异步的。但我认为这不会引起任何问题 - 它在本地和我的其他项目中都有效。

无论我注入(inject)的第一个自定义服务是什么,Azure 上的依赖注入(inject)总是失败。我改变了服务,调换了它们等等,无论我要求什么,它都没有启动。如果我从构造函数中删除任何自定义服务并只保留记录器,一切都会正常工作。调试时,自定义服务在本地 webapi 项目、azure 以及本地函数项目中 100% 工作。

Result: Failure Exception: System.InvalidOperationException: Unable to resolve service for type 'medimore_azure.FormRecogniserService' while attempting to activate 'medimore_azure_functions.Messaging.ProcessingAnalyseDocumentsConsumer'. at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters) at Microsoft.Azure.Functions.Worker.DefaultFunctionActivator.CreateInstance(Type instanceType, FunctionContext context) in D:\a_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionActivator.cs:line 23 at Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionInvoker2.CreateInstance(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionInvoker.cs:line 26 at Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor.ExecuteAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionExecutor.cs:line 30 at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a\_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13 at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 77 at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a\_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 88 Stack: at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters) at Microsoft.Azure.Functions.Worker.DefaultFunctionActivator.CreateInstance(Type instanceType, FunctionContext context) in D:\a\_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionActivator.cs:line 23 at Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionInvoker2.CreateInstance(FunctionContext context) in D:\a_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionInvoker.cs:line 26 at Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor.ExecuteAsync(FunctionContext context) in D:\a_work\1\s\src\DotNetWorker.Core\Invocation\DefaultFunctionExecutor.cs:line 30 at Microsoft.Azure.Functions.Worker.OutputBindings.OutputBindingsMiddleware.Invoke(FunctionContext context, FunctionExecutionDelegate next) in D:\a_work\1\s\src\DotNetWorker.Core\OutputBindings\OutputBindingsMiddleware.cs:line 13 at Microsoft.Azure.Functions.Worker.FunctionsApplication.InvokeFunctionAsync(FunctionContext context) in D:\a_work\1\s\src\DotNetWorker.Core\FunctionsApplication.cs:line 77 at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 88

最佳答案

正如 @Steven 所说,“在配置容器的过程中调用 BuildServiceProvider 通常不是一个好主意”。它可以在本地运行,但在部署时会崩溃。重构了很多配置部分,因为我删除了 BuildServiceProvider 调用,所以它按预期工作。

关于azure - .net 7 隔离的 Azure 函数依赖注入(inject)在部署时失败(在本地工作),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/77151093/

相关文章:

c# - 将 IIS 与 .NET Core 2.2 Web API 一起使用时自动生成的 web.config 中的硬编码路径

azure - 两个 Azure 事件中心使用者会收到相同的通知吗?

Azure 流分析输入 Blob 存储动态路径模式

azure - 使用 Azure 应用程序网关修改/截断基于路径的路由中的路径

asp.net-core - .NET Core project.json 命令 - 设置 ASPNETCORE_ENVIRONMENT

c# - 无法解析作用域服务 DbContextOptions

azure - Azure APIM 是否支持 GraphQL?

azure - VSO 构建管道的 Nuget 恢复失败

c# - 这可以被视为依赖注入(inject)的一种简单形式吗?

dependency-injection - 使用 ASP.NET Core DI 注册 MediatR 管道约束后处理器