Azure Functions 支持依赖项注入(DI)软件设计模式,这是实现类与其依赖项之间的 控制反转(IoC) 的技术。
Azure Functions 中的依赖关系注入基于 .NET Core 依赖关系注入功能构建。 建议熟悉 .NET Core 依赖项注入 。 代替依赖项的方式与使用 Azure Functions 对消耗计划读取配置值的方式之间存在差异。
对依赖项注入的支持从 Azure Functions 2.x 开始。
重要
本文中的指南仅适用于使用运行时在进程内运行的 C# 类库函数。 此自定义依赖项注入模型不适用于 .NET 隔离函数,这允许在进程外运行 .NET 函数。 .NET 隔离的工作进程模型依赖于常规 ASP.NET 核心依赖项注入模式。 若要了解详细信息,请参阅 .NET 独立工作进程指南中的 依赖关系注入 。
先决条件
在使用依赖项注入之前,必须安装以下 NuGet 包:
Microsoft.NET.Sdk.Functions 包 1.0.28 或更高版本
Microsoft.Extensions.DependencyInjection (目前仅支持版本 2.x 或更高版本)
注册服务
若要注册服务,请创建用于配置组件并将组件添加到 IFunctionsHostBuilder
实例的方法。 Azure Functions 主机创建一个实例 IFunctionsHostBuilder
并将其直接传递到方法中。
警告
对于在消耗计划或高级计划中运行的函数应用,对触发器中使用的配置值的修改可能会导致缩放错误。 类 FunctionsStartup
对这些属性所做的任何更改都会导致函数应用启动错误。
注入IConfiguration
可能导致意外行为。 若要详细了解如何添加配置源,请参阅 自定义配置源。
若要注册该方法,请添加 FunctionsStartup
程序集属性,该属性指定在启动期间使用的类型名称。
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace;
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton<IMyService>((s) => {
return new MyService();
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
此示例使用在启动时注册 HttpClient
所需的 Microsoft.Extensions.Http 包。
注意事项
在运行时处理启动类之前和之后会运行一系列注册步骤。 因此,请记住以下各项:
启动类仅用于设置和注册。 避免在启动过程中使用在启动时注册的服务。 例如,请勿尝试在启动过程中注册的日志记录器中记录消息。 在注册过程的这一阶段,您的服务尚未可用。
Configure
运行该方法后,Functions 运行时将继续注册其他依赖项,这可能会影响服务的运作方式。依赖项注入容器仅保留显式注册的类型。 唯一可用作可注入类型的服务是在
Configure
方法中设置的服务。 因此,像BindingContext
和ExecutionContext
这样的函数特定类型在设置过程中或作为可注入类型时都不可用。不支持配置 ASP.NET 身份验证。 Functions 主机会配置 ASP.NET 身份验证服务,以适当公开用于核心生命周期操作的 API。 自定义
Startup
类中的其他配置可以替代此配置,从而导致意外后果。 例如,调用builder.Services.AddAuthentication()
可能会中断门户和主机之间的身份验证,导致出现“Azure Functions 运行时无法访问”等消息。
使用注入的依赖项
构造函数注入用于使依赖项在函数中可用。 使用构造函数注入要求不要对注入的服务或函数类使用静态类。
以下示例演示如何将 IMyService
和 HttpClient
依赖项注入到一个由 HTTP 触发的函数中。
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Threading.Tasks;
namespace MyNamespace;
public class MyHttpTrigger
{
private readonly HttpClient _client;
private readonly IMyService _service;
public MyHttpTrigger(IHttpClientFactory httpClientFactory, IMyService service)
{
this._client = httpClientFactory.CreateClient();
this._service = service;
}
[FunctionName("MyHttpTrigger")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
var response = await _client.GetAsync("https://microsoft.com");
var message = _service.GetMessage();
return new OkObjectResult("Response from function with injected dependencies.");
}
}
此示例使用 Microsoft.Extensions.Http 包,该包用于在启动时注册HttpClient
。
服务生存期
Azure Functions 应用提供 与 ASP.NET 依赖项注入相同的服务生存期。 对于 Functions 应用程序,不同的服务生存期行为如下:
- 瞬态:瞬态服务是在每次解析服务时创建的。
- 限定范围:范围内服务生存期与函数执行生存期匹配。 每次执行函数后,都会创建设有范围的服务。 稍后在执行期间针对该服务的请求将重复使用现有服务实例。
- 单一实例:单一实例服务生存期与主机生存期匹配,并在该实例上的函数执行中重复使用。 建议为连接和客户端使用单一实例生存期服务,例如
DocumentClient
或HttpClient
实例。
在 GitHub 上查看或下载 不同服务生存期的示例 。
日志记录服务
如果需要自己的日志记录提供程序,请将自定义类型注册为 ILoggerProvider
(可通过 Microsoft.Extensions.Logging.Abstractions NuGet 包获取)的实例。
Application Insights 由 Azure Functions 自动添加。
警告
- 不要将
AddApplicationInsightsTelemetry()
添加到服务集合中,因为该集合注册的服务会与环境提供的服务发生冲突。 - 请勿注册您自己的
TelemetryConfiguration
或TelemetryClient
,如果您正在使用内置的 Application Insights 功能。 如果需要配置自己的TelemetryClient
实例,请通过插入的TelemetryConfiguration
创建一个实例,如在 C# 函数中记录自定义遥测中所示。
ILogger<T> 和 ILoggerFactory
主机将 ILogger<T>
和服务 ILoggerFactory
注入到构造函数中。 但是,默认情况下,这些新的日志记录筛选器会从函数日志中筛选出来。 需要修改 host.json
文件以选择加入额外的筛选器和类别。
下面的示例演示如何添加包含向主机公开的日志的 ILogger<HttpTrigger>
。
namespace MyNamespace;
public class HttpTrigger
{
private readonly ILogger<HttpTrigger> _log;
public HttpTrigger(ILogger<HttpTrigger> log)
{
_log = log;
}
[FunctionName("HttpTrigger")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req)
{
_log.LogInformation("C# HTTP trigger function processed a request.");
// ...
}
以下示例 host.json
文件添加日志筛选器。
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
},
"logLevel": {
"MyNamespace.HttpTrigger": "Information"
}
}
}
有关日志级别的详细信息,请参阅 “配置日志级别”。
函数应用提供的服务
函数宿主注册了许多服务。 以下服务可以安全地作为应用程序中的依赖项:
服务类型 | 生存期 | DESCRIPTION |
---|---|---|
Microsoft.Extensions.Configuration.IConfiguration |
单身 人士 | 运行时配置 |
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider |
单身 人士 | 负责提供主机实例的 ID |
如果要依赖其他服务, 请创建问题并在 GitHub 上提出它们。
替代主机服务
当前不支持替代主机提供的服务。 如果要替代某些服务, 请在 GitHub 上创建问题并提出它们。
使用选项和设置
应用设置中定义的值在实例中IConfiguration
可用,这样就可以在启动类中读取应用设置值。
可以从IConfiguration
实例中提取值到自定义类型。 将应用设置值复制到自定义类型可使这些值可注入,从而轻松测试服务。 读入到配置实例中的设置必须是简单的键/值对。 对于在弹性高级计划中运行的函数,应用程序设置名称只能包含字母、数字(0-9
)、句点(.
)、冒号(:
)和下划线(_
)。 有关详细信息,请参阅 应用设置注意事项。
请考虑以下类,该类包含一个名为与应用设置一致的属性:
public class MyOptions
{
public string MyCustomSetting { get; set; }
}
以及一个 local.settings.json
文件,它可以按如下所示构造自定义设置:
{
"IsEncrypted": false,
"Values": {
"MyOptions:MyCustomSetting": "Foobar"
}
}
在Startup.Configure
方法内部,可以使用以下代码将IConfiguration
实例中的值提取到自定义类型中:
builder.Services.AddOptions<MyOptions>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("MyOptions").Bind(settings);
});
调用 Bind
将具有匹配属性名称的值从配置复制到自定义实例中。 选项实例现已在 IoC 容器中提供,用于注入函数。
options 对象作为泛型 IOptions
接口的实例注入到函数中。 使用 Value
属性访问配置中找到的值。
using System;
using Microsoft.Extensions.Options;
public class HttpTrigger
{
private readonly MyOptions _settings;
public HttpTrigger(IOptions<MyOptions> options)
{
_settings = options.Value;
}
}
有关详细信息,请参阅 ASP.NET Core 中的选项模式。
使用 ASP.NET Core 用户机密
在本地开发应用时,ASP.NET Core 提供了一个 机密管理器工具 ,可用于将机密信息存储在项目根目录之外。 这使得机密意外提交到源代码管理的可能性更小。 Azure Functions Core Tools(版本 3.0.3233 或更高版本)会自动读取由 ASP.NET 核心机密管理器创建的机密。
若要将 .NET Azure Functions 项目配置为使用用户机密,请在项目根目录中运行以下命令。
dotnet user-secrets init
然后使用dotnet user-secrets set
命令来创建或更新机密。
dotnet user-secrets set MySecret "my secret value"
若要访问函数应用代码中的用户机密值,请使用 IConfiguration
或 IOptions
。
自定义配置源
若要指定其他配置源,请替代函数应用的 StartUp
类中的 ConfigureAppConfiguration
方法。
以下示例从基本和可选的特定于环境的应用设置文件中添加配置值。
using System.IO;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace;
public class Startup : FunctionsStartup
{
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
FunctionsHostBuilderContext context = builder.GetContext();
builder.ConfigurationBuilder
.AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
.AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
.AddEnvironmentVariables();
}
public override void Configure(IFunctionsHostBuilder builder)
{
}
}
将配置提供程序添加到 IFunctionsConfigurationBuilder
的 ConfigurationBuilder
属性。 有关使用配置提供程序的详细信息,请参阅 ASP.NET Core 中的配置。
FunctionsHostBuilderContext
是从 IFunctionsConfigurationBuilder.GetContext()
中获取的。 使用此上下文检索当前环境名称,并解析函数应用文件夹中配置文件的位置。
默认情况下,配置文件(例如 appsettings.json
不会自动复制到函数应用的输出文件夹)。 请更新你的.csproj
文件以匹配以下示例,以确保文件能够被复制。
<None Update="appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="appsettings.Development.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
后续步骤
有关详细信息,请参阅以下资源: