Autofac Azure Functions 集成
Azure Functions 支持使用 Microsoft 的依赖注入框架进行依赖注入,但你可以通过添加一些引导代码使其与 Autofac 一起工作。
我们建议阅读官方 Microsoft 文档 《Azure Functions 中的依赖注入概述》 ,以了解 Azure Functions 中依赖注入的基本概念。
步骤概览
- 从 NuGet 安装 Autofac、
Autofac.Extensions.DependencyInjection
和Microsoft.Azure.Functions.Extensions
。 - 添加一个基于 Autofac 的工作器激活器,用于创建你的函数类实例。
- 创建一个
Startup
类,在其中注册你的组件,并替换默认的工作器激活器。
Autofac 工作器激活器
工作器激活器负责实例化包含你的函数的类。在项目中添加以下代码——它是一个基于 Autofac 生命周期范围的工作器激活器,用于从生命周期范围中获取适当的类。接下来我们将实现 LifetimeScopeWrapper
和 LoggerModule
。
internal class AutofacJobActivator : IJobActivatorEx
{
public T CreateInstance<T>()
{
// 在实际应用中,这个方法不会被调用。因为无法安全地在此处解析T(因为我们没有访问ILifetimeScope),所以最好直接抛出异常。
throw new NotSupportedException();
}
public T CreateInstance<T>(IFunctionInstanceEx functionInstance)
where T : notnull
{
var lifetimeScope = functionInstance.InstanceServices
.GetRequiredService<LifetimeScopeWrapper>()
.Scope;
// 这是必要的,因为ILoggerFactory的一些依赖项是在FunctionsStartup之后注册的。
var loggerFactory = functionInstance.InstanceServices.GetRequiredService<ILoggerFactory>();
lifetimeScope.Resolve<ILoggerFactory>(
new NamedParameter(LoggerModule.LoggerFactoryParam, loggerFactory)
);
lifetimeScope.Resolve<ILogger>(
new NamedParameter(LoggerModule.FunctionNameParam, functionInstance.FunctionDescriptor.LogName)
);
return lifetimeScope.Resolve<T>();
}
}
接下来,实现 LifetimeScopeWrapper
。这个类从 IServiceCollection
中解析,允许我们在函数执行完毕后释放 Autofac 生命周期范围。
internal sealed class LifetimeScopeWrapper : IDisposable
{
public ILifetimeScope Scope { get; }
public LifetimeScopeWrapper(IContainer container)
{
Scope = container.BeginLifetimeScope();
}
public void Dispose()
{
Scope.Dispose();
}
}
为了能够解析 ILogger
,我们需要特殊处理,因为某些日志工厂直到 Startup
类运行后才会初始化。我们可以通过添加以下代码来解决这个问题。
internal class LoggerModule : Module
{
public const string LoggerFactoryParam = "loggerFactory";
public const string FunctionNameParam = "functionName";
protected override void Load(ContainerBuilder builder)
{
builder.Register((ctx, p) => p.Named<ILoggerFactory>(LoggerFactoryParam))
.SingleInstance();
builder.Register((ctx, p) =>
{
var factory = ctx.Resolve<ILoggerFactory>();
var functionName = p.Named<string>(FunctionNameParam);
return factory.CreateLogger(Microsoft.Azure.WebJobs.Logging.LogCategories.CreateFunctionUserCategory(functionName));
})
.InstancePerLifetimeScope();
}
}
即使你没有直接使用 ILogger
,也应该将 LoggerModule
包含在项目中,因为 Microsoft 的许多 NuGet 包都引用了此接口。
Startup
类
最后,添加一个 Startup
类将所有内容连接起来。这个类在概念上类似于 ASP.NET Core 项目的 Startup
类。
FunctionsStartup
基类由 Microsoft.Azure.Functions.Extensions
NuGet 包提供。
[assembly: FunctionsStartup(typeof(MyFunctionApp.Startup))]
namespace MyFunctionApp;
internal class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
// 使用IServiceCollection.Add扩展方法按需添加功能,例如:
builder.Services.AddDataProtection();
builder.Services.AddSingleton(GetContainer(builder.Services));
// 重要:使用AddScoped,以便我们的Autofac生命周期范围在函数执行完毕时被释放
builder.Services.AddScoped<LifetimeScopeWrapper>();
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivator), typeof(AutofacJobActivator)));
builder.Services.Replace(ServiceDescriptor.Singleton(typeof(IJobActivatorEx), typeof(AutofacJobActivator)));
}
private static IContainer GetContainer(IServiceCollection serviceCollection)
{
var containerBuilder = new ContainerBuilder();
containerBuilder.Populate(serviceCollection);
containerBuilder.RegisterModule<LoggerModule>();
// 这是一种方便的方法,一次注册所有你的函数类
containerBuilder.RegisterAssemblyTypes(typeof(Startup).Assembly)
.InNamespaceOf<Function1>();
// TODO:像正常一样使用ContainerBuilder注册其他依赖项
return containerBuilder.Build();
}
}
就这样!现在你的函数类将从 Autofac 中解析。
示例函数
这是一个使用依赖注入服务的 HTTP 触发函数示例。请注意,类和 Run
方法不是静态的。
public class Function1
{
private readonly IRandomNumberService _randomNumberService;
public Function1(IRandomNumberService randomNumberService)
{
_randomNumberService = randomNumberService;
}
// 通过在浏览器中访问http://localhost:7071/api/Function1来调用此方法
[FunctionName("Function1")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = null)]
HttpRequest request
)
{
var number = _randomNumberService.GetDouble();
return new OkObjectResult($"Your random number is {number}.");
}
}
致谢
本指南受到了社区 NuGet 包 Autofac.Extensions.DependencyInjection.AzureFunctions 的启发。如果你更喜欢使用 NuGet 包而不是这里介绍的自定义方法,可以尝试一下 Autofac.Extensions.DependencyInjection.AzureFunctions
。