.NET 7独立Azure函数部署后依赖注入失败(本地正常)
我有一个Web API项目和独立的Azure函数项目,两者依赖注入(DI)配置几乎一致,还提取了扩展方法来加载依赖。函数由ServiceBus消息触发,本地调试完全正常,但部署到Azure后,发送消息就会报DI错误,无法实例化自定义类。
消息监听函数代码
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(); //扩展方法,用于添加服务 services.AddHttpContextAccessor(); //使HttpContext可用 services.ServiceDependencies(); //扩展方法,用于添加服务 }); await host.Build().RunAsync();
DI扩展方法代码
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; }
异常信息
结果:失败 异常信息:System.InvalidOperationException: 尝试激活'medimore_azure_functions.Messaging.ProcessingAnalyseDocumentsConsumer'时,无法解析类型'medimore_azure.FormRecogniserService'的服务。 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.DefaultFunctionInvoker
2.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 堆栈跟踪: 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
补充说明
- 本地调试完全正常,WebAPI项目无论本地还是Azure部署都正常,只有Azure函数部署后出问题
- 试过移除DebugKeyVault切换逻辑,部署后依然报错
- 自定义服务依赖KeyVault密钥,所以用了异步扩展方法,本地和WebAPI都没问题
- 只要移除所有自定义服务注入,只保留Logger,函数就能正常运行
1. 停止在DI配置中重复构建ServiceProvider
你的扩展方法里多次调用services.BuildServiceProvider(),会创建独立的服务容器实例,导致后续注册的服务无法被主容器识别。Azure函数宿主环境对容器生命周期的管控更严格,这直接引发了解析失败,而本地环境的容器行为掩盖了问题。
修改扩展方法,改用工厂模式延迟解析依赖,不要提前构建容器:
public static async Task<IServiceCollection> AddSensitiveServicesAsync(this IServiceCollection services) { // 用工厂委托延迟创建BlobStorageService,避免提前构建容器 services.AddTransient<BlobStorageService>(async sp => { var keyVault = sp.GetRequiredService<IKeyVaultService>(); var connString = await keyVault.GetSecret("blob-storage-connection"); return new BlobStorageService(connString); }); // 同样用工厂模式创建FormRecogniserService services.AddTransient<FormRecogniserService>(async sp => { var keyVault = sp.GetRequiredService<IKeyVaultService>(); var secretKey = await keyVault.GetSecret("form-recogniser-key"); var endpoint = await keyVault.GetSecret("form-recogniser-endpoint"); var blobStorage = sp.GetRequiredService<BlobStorageService>(); return new FormRecogniserService(secretKey, endpoint, blobStorage); }); return services; }
2. 验证KeyVault访问权限
在Azure门户中,确认函数应用的托管标识已被授予KeyVault的机密读取权限。如果权限缺失,会导致密钥获取失败,进而无法实例化依赖服务,最终表现为DI解析失败。
3. 对齐依赖包版本
确保Azure函数相关包(Microsoft.Azure.Functions.Worker、Microsoft.Azure.Functions.Worker.Sdk等)与WebAPI项目的依赖包版本一致,版本不兼容可能导致DI行为差异。
4. 优先使用接口注入
虽然直接注入具体类型本身没问题,但推荐使用接口(比如IFormRecogniserService),符合依赖倒置原则的同时,也能避免类型注册相关的解析问题:
// 修改构造函数参数为接口类型 public ProcessingAnalyseDocumentsConsumer( ILogger<ProcessingAnalyseDocumentsConsumer> logger, IFormRecogniserService formRecogniserService, IReprocessEncounterService reprocessEncounterService, DataContext context) { // ... } // DI注册时添加接口与实现的映射 services.AddTransient<IFormRecogniserService, FormRecogniserService>();
内容的提问来源于stack exchange,提问作者spaceman

