在 Azure Functions V2 上注册第三方 DI Framework (Lamar/Autofac) 的正确方法
Proper way of registering 3rd party DI Framework (Lamar/Autofac) on Azure functions V2
Azure Functions V2 现在支持 .net dependency injection
为了实现,您需要执行以下代码:
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
我想将默认容器从 .net 更改为 "Lamar" DI 框架。
On their documentation they have an example for a WebHost:
var builder = new WebHostBuilder();
builder
// Replaces the built in DI container
// with Lamar
.UseLamar()
// Normal ASP.Net Core bootstrapping
.UseUrls("http://localhost:5002")
.UseKestrel()
.UseStartup<Startup>();
builder.Start();
但我无法更改 IFunctionsHostBuilder 以使用 "UseLamar()" 扩展。因为这扩展了 IWebHostBuilder。
我能够拦截 azure 函数初始化的唯一方法是使用配置 IFunctionsHostBuilder 的 FunctionsStartup 或配置 IWebJobsBuilder 的 IWebJobsStartup ,但我在 Lamar 上找不到此类构建的扩展。
我试图检查现有的扩展来创建类似的代码,但没有用,因为可能我需要创建更多的东西:
[assembly: FunctionsStartup(typeof(FunctionAppPrototype.Startup))]
namespace FunctionAppPrototype
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new Container(x =>
{
x.AddTransient<IMyService, MyService>();
});
builder.Services.AddSingleton<IServiceProviderFactory<IServiceCollection>, LamarServiceProviderFactory>();
builder.Services.AddSingleton<IServiceProviderFactory<ServiceRegistry>, LamarServiceProviderFactory>();
}
}
}
经过一些研究,我找到了使用 Autofac 的解决方案。我无法使用 Lamar 做到这一点,它没有针对 IFunctionsHostBuilder 或 IWebJobsBuilder 的扩展。
源代码:Binding extensions for dependency injection in Azure Function v2
Nuget:Willezone.Azure.WebJobs.Extensions.DependencyInjection
首先需要拦截函数app的启动,代码如下:
[assembly: WebJobsStartup(typeof(AutoFacFunctionAppPrototype.WebJobsStartup))]
namespace AutoFacFunctionAppPrototype
{
public class WebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection<AutoFacServiceProviderBuilder>();
}
}
然后创建容器并注册依赖:
namespace AutoFacFunctionAppPrototype.Builders
{
public class AutoFacServiceProviderBuilder : IServiceProviderBuilder
{
private readonly IConfiguration configuration;
public AutoFacServiceProviderBuilder(IConfiguration configuration)
=> this.configuration = configuration;
public IServiceProvider Build()
{
var services = new ServiceCollection();
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
var builder = new ContainerBuilder();
builder.RegisterType<SingletonService>().As<ISingletonService>().SingleInstance();
builder.Populate(services); // Populate is needed to have support for scopes.
return new AutofacServiceProvider(builder.Build());
}
}
}
然后你可以在使用属性 [Inject]:
的函数上使用它们
namespace AutoFacFunctionAppPrototype.Functions
{
public static class CounterFunction
{
[FunctionName("Counter")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
[Inject]ITransientService transientService,
[Inject]IScopedService scopedService,
[Inject]ISingletonService singletonService,
ILogger logger)
{
logger.LogInformation("C# HTTP trigger function processed a request.");
string result = String.Join(Environment.NewLine, new[] {
$"Transient: {transientService.GetCounter()}",
$"Scoped: {scopedService.GetCounter()}",
$"Singleton: {singletonService.GetCounter()}",
});
return new OkObjectResult(result);
}
}
}
使用这种方法我只能注入参数,无法进行构造函数或 属性 注入,即使我是非静态 class.
注意:如果将来 Autofac 支持 IFunctionsHostBuilder 的扩展,可能最好使用该方法而不是 IWebJobsStartup。
我创建了一种在 Azure Functions v3 项目中使用 Autofac DI 的新方法,无需使用静态函数或注入属性。使用适当的范围调用服务的处置。
GitHub: https://github.com/junalmeida/autofac-azurefunctions
NuGet:https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions
随时与我一起贡献!
示例
public class Function1
{
private readonly IService1 _service1;
public Function1(IService1 service1)
{
_service1 = service1;
}
[FunctionName(nameof(Function1))]
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem, ILogger log)
{
...
}
Azure Functions V2 现在支持 .net dependency injection
为了实现,您需要执行以下代码:
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
我想将默认容器从 .net 更改为 "Lamar" DI 框架。
On their documentation they have an example for a WebHost:
var builder = new WebHostBuilder();
builder
// Replaces the built in DI container
// with Lamar
.UseLamar()
// Normal ASP.Net Core bootstrapping
.UseUrls("http://localhost:5002")
.UseKestrel()
.UseStartup<Startup>();
builder.Start();
但我无法更改 IFunctionsHostBuilder 以使用 "UseLamar()" 扩展。因为这扩展了 IWebHostBuilder。 我能够拦截 azure 函数初始化的唯一方法是使用配置 IFunctionsHostBuilder 的 FunctionsStartup 或配置 IWebJobsBuilder 的 IWebJobsStartup ,但我在 Lamar 上找不到此类构建的扩展。
我试图检查现有的扩展来创建类似的代码,但没有用,因为可能我需要创建更多的东西:
[assembly: FunctionsStartup(typeof(FunctionAppPrototype.Startup))]
namespace FunctionAppPrototype
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new Container(x =>
{
x.AddTransient<IMyService, MyService>();
});
builder.Services.AddSingleton<IServiceProviderFactory<IServiceCollection>, LamarServiceProviderFactory>();
builder.Services.AddSingleton<IServiceProviderFactory<ServiceRegistry>, LamarServiceProviderFactory>();
}
}
}
经过一些研究,我找到了使用 Autofac 的解决方案。我无法使用 Lamar 做到这一点,它没有针对 IFunctionsHostBuilder 或 IWebJobsBuilder 的扩展。
源代码:Binding extensions for dependency injection in Azure Function v2
Nuget:Willezone.Azure.WebJobs.Extensions.DependencyInjection
首先需要拦截函数app的启动,代码如下:
[assembly: WebJobsStartup(typeof(AutoFacFunctionAppPrototype.WebJobsStartup))]
namespace AutoFacFunctionAppPrototype
{
public class WebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection<AutoFacServiceProviderBuilder>();
}
}
然后创建容器并注册依赖:
namespace AutoFacFunctionAppPrototype.Builders
{
public class AutoFacServiceProviderBuilder : IServiceProviderBuilder
{
private readonly IConfiguration configuration;
public AutoFacServiceProviderBuilder(IConfiguration configuration)
=> this.configuration = configuration;
public IServiceProvider Build()
{
var services = new ServiceCollection();
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
var builder = new ContainerBuilder();
builder.RegisterType<SingletonService>().As<ISingletonService>().SingleInstance();
builder.Populate(services); // Populate is needed to have support for scopes.
return new AutofacServiceProvider(builder.Build());
}
}
}
然后你可以在使用属性 [Inject]:
的函数上使用它们namespace AutoFacFunctionAppPrototype.Functions
{
public static class CounterFunction
{
[FunctionName("Counter")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
[Inject]ITransientService transientService,
[Inject]IScopedService scopedService,
[Inject]ISingletonService singletonService,
ILogger logger)
{
logger.LogInformation("C# HTTP trigger function processed a request.");
string result = String.Join(Environment.NewLine, new[] {
$"Transient: {transientService.GetCounter()}",
$"Scoped: {scopedService.GetCounter()}",
$"Singleton: {singletonService.GetCounter()}",
});
return new OkObjectResult(result);
}
}
}
使用这种方法我只能注入参数,无法进行构造函数或 属性 注入,即使我是非静态 class.
注意:如果将来 Autofac 支持 IFunctionsHostBuilder 的扩展,可能最好使用该方法而不是 IWebJobsStartup。
我创建了一种在 Azure Functions v3 项目中使用 Autofac DI 的新方法,无需使用静态函数或注入属性。使用适当的范围调用服务的处置。
GitHub: https://github.com/junalmeida/autofac-azurefunctions
NuGet:https://www.nuget.org/packages/Autofac.Extensions.DependencyInjection.AzureFunctions
随时与我一起贡献!
示例
public class Function1
{
private readonly IService1 _service1;
public Function1(IService1 service1)
{
_service1 = service1;
}
[FunctionName(nameof(Function1))]
public async Task Run([QueueTrigger("myqueue-items", Connection = "AzureWebJobsStorage")]string myQueueItem, ILogger log)
{
...
}