在 .NET Azure Functions 中使用依赖项注入Use dependency injection in .NET Azure Functions

Azure Functions 支持依赖项注入 (DI) 软件设计模式,这是一种在类与其依赖项之间实现控制反转 (IoC) 的方法。Azure Functions supports the dependency injection (DI) software design pattern, which is a technique to achieve Inversion of Control (IoC) between classes and their dependencies.

  • Azure Functions 中的依赖项注入基于 .NET Core 依赖项注入功能构建。Dependency injection in Azure Functions is built on the .NET Core Dependency Injection features. 建议熟悉 .NET Core 依赖项注入Familiarity with .NET Core dependency injection is recommended. 两者在如何替代依赖项以及如何使用 Azure Functions 对消耗计划读取配置值方面存在差异。There are differences in how you override dependencies and how configuration values are read with Azure Functions on the Consumption plan.

  • 从 Azure Functions 2.x 支持依赖项注入。Support for dependency injection begins with Azure Functions 2.x.

先决条件Prerequisites

必须先安装以下 NuGet 包,然后才能使用依赖项注入:Before you can use dependency injection, you must install the following NuGet packages:

注册服务Register services

若要注册服务,请创建一个方法来配置组件并将组件添加到 IFunctionsHostBuilder 实例。To register services, create a method to configure and add components to an IFunctionsHostBuilder instance. Azure Functions 主机会创建 IFunctionsHostBuilder 的实例,并将其直接传递到方法中。The Azure Functions host creates an instance of IFunctionsHostBuilder and passes it directly into your method.

若要注册方法,请添加 FunctionsStartup 程序集属性来指定在启动期间使用的类型名称。To register the method, add the FunctionsStartup assembly attribute that specifies the type name used during startup.

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 包。This example uses the Microsoft.Extensions.Http package required to register an HttpClient at startup.

注意事项Caveats

在运行时处理启动类之前和之后会运行一系列注册步骤。A series of registration steps run before and after the runtime processes the startup class. 因此,请注意以下事项:Therefore, keep in mind the following items:

  • 启动类仅用于设置和注册。The startup class is meant for only setup and registration. 避免使用在启动时注册的服务。Avoid using services registered at startup during the startup process. 例如,请勿尝试在记录器中记录在启动过程中注册的消息。For instance, don't try to log a message in a logger that is being registered during startup. 此注册过程点对你的服务来说太早,因此无法使用。This point of the registration process is too early for your services to be available for use. 在运行 Configure 方法以后,Functions 运行时会继续注册其他依赖项,这可能影响服务的运行。After the Configure method is run, the Functions runtime continues to register additional dependencies, which can affect how your services operate.

  • 依赖项注入容器仅存储显式注册的类型。The dependency injection container only holds explicitly registered types . 能够用作可注入类型的服务仅限在 Configure 方法中设置的服务。The only services available as injectable types are what are setup in the Configure method. 因此,特定于 Functions 的类型(例如 BindingContextExecutionContext)在设置中不可用,也不能用作可注入类型。As a result, Functions-specific types like BindingContext and ExecutionContext aren't available during setup or as injectable types.

使用注入的依赖项Use injected dependencies

使用构造函数注入以后,依赖项即可在函数中使用。Constructor injection is used to make your dependencies available in a function. 使用构造函数注入要求你不要对已注入服务或对函数类使用静态类。The use of constructor injection requires that you do not use static classes for injected services or for your function classes.

以下示例演示了 IMyServiceHttpClient 依赖项是如何注入到 HTTP 触发的函数中的。The following sample demonstrates how the IMyService and HttpClient dependencies are injected into an HTTP-triggered function.

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(HttpClient httpClient, IMyService service)
        {
            this._client = httpClient;
            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.");
        }
    }
}

此示例使用在启动时注册 HttpClient 所需的 Microsoft.Extensions.Http 包。This example uses the Microsoft.Extensions.Http package required to register an HttpClient at startup.

服务生存期Service lifetimes

Azure Functions 应用提供与 ASP.NET 依赖项注入相同的服务生存期。Azure Functions apps provide the same service lifetimes as ASP.NET Dependency Injection. 就 Functions 应用来说,不同的服务生存期表现如下:For a Functions app, the different service lifetimes behave as follows:

  • 暂时性 :每次请求此服务时,都会创建暂时性服务。Transient : Transient services are created upon each request of the service.
  • 限定范围 :限定范围的服务的生存期与函数执行生存期相匹配。Scoped : The scoped service lifetime matches a function execution lifetime. 作用域服务在每次执行时创建一次。Scoped services are created once per execution. 在执行期间对该服务的后续请求会重复使用现有服务实例。Later requests for that service during the execution reuse the existing service instance.
  • 单一实例 :单一实例服务生存期与主机生存期相匹配,并且在该实例上的各个函数执行之间重用。Singleton : The singleton service lifetime matches the host lifetime and is reused across function executions on that instance. 对于连接和客户端(例如 DocumentClientHttpClient 实例),建议使用单一实例生存期服务。Singleton lifetime services are recommended for connections and clients, for example DocumentClient or HttpClient instances.

在 GitHub 上查看或下载不同服务生存期的示例View or download a sample of different service lifetimes on GitHub.

日志记录服务Logging services

如果需要自己的日志记录提供程序,请将自定义类型注册为 ILoggerProvider(可通过 Microsoft.Extensions.Logging.Abstractions NuGet 包获取)的实例。If you need your own logging provider, register a custom type as an instance of ILoggerProvider, which is available through the Microsoft.Extensions.Logging.Abstractions NuGet package.

Azure Functions 会自动添加 Application Insights。Application Insights is added by Azure Functions automatically.

警告

  • 请勿将 AddApplicationInsightsTelemetry() 添加到服务集合,因为它注册的服务与环境提供的服务发生冲突。Don't add AddApplicationInsightsTelemetry() to the services collection, which registers services that conflict with services provided by the environment.
  • 如果使用内置 Application Insights 功能,请勿注册自己的 TelemetryConfigurationTelemetryClientDon't register your own TelemetryConfiguration or TelemetryClient if you are using the built-in Application Insights functionality. 如果需要配置自己的 TelemetryClient 实例,请通过插入的 TelemetryConfiguration 创建一个实例,如在 C# 函数中记录自定义遥测中所示。If you need to configure your own TelemetryClient instance, create one via the injected TelemetryConfiguration as shown in Log custom telemetry in C# functions.

ILogger 和 ILoggerFactoryILogger and ILoggerFactory

主机会将 ILogger<T>ILoggerFactory 服务注入构造函数中。The host injects ILogger<T> and ILoggerFactory services into constructors. 但是在默认情况下,会从函数日志中筛选出新日志记录筛选器。However, by default these new logging filters are filtered out of the function logs. 需要修改 host.json 文件,以选择加入其他筛选器和类别。You need to modify the host.json file to opt-in to additional filters and categories.

下面的示例演示如何添加包含向主机公开的日志的 ILogger<HttpTrigger>The following example demonstrates how to add an ILogger<HttpTrigger> with logs that are exposed to the host.

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 文件添加日志筛选器。The following example host.json file adds the log filter.

{
    "version": "2.0",
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            }
        },
        "logLevel": {
            "MyNamespace.HttpTrigger": "Information"
        }
    }
}

函数应用提供的服务Function app provided services

函数主机注册许多服务。The function host registers many services. 以下服务可以安全地用作应用程序中的依赖项:The following services are safe to take as a dependency in your application:

服务类型Service Type 生存期Lifetime 说明Description
Microsoft.Extensions.Configuration.IConfiguration 单一实例Singleton 运行时配置Runtime configuration
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider 单一实例Singleton 负责提供主机实例的 IDResponsible for providing the ID of the host instance

如果存在你要依赖的其他服务,请在 GitHub 上创建一个问题并提出它们If there are other services you want to take a dependency on, create an issue and propose them on GitHub.

替代主机服务Overriding host services

目前不支持替代由主机提供的服务。Overriding services provided by the host is currently not supported. 如果有要替代的服务,请在 GitHub上创建并提出问题If there are services you want to override, create an issue and propose them on GitHub.

使用选项和设置Working with options and settings

应用设置中定义的值可以在 IConfiguration 实例中使用,这使得你可以读取启动类中的应用设置值。Values defined in app settings are available in an IConfiguration instance, which allows you to read app settings values in the startup class.

可以将 IConfiguration 实例中的值提取为自定义类型。You can extract values from the IConfiguration instance into a custom type. 如果将应用设置值复制到自定义类型,则通过使这些值可注入,可以轻松地测试服务。Copying the app settings values to a custom type makes it easy test your services by making these values injectable. 读入到配置实例中的设置必须是简单的键/值对。Settings read into the configuration instance must be simple key/value pairs.

请考虑以下类,其中包含一个命名与应用设置一致的属性:Consider the following class that includes a property named consistent with an app setting:

public class MyOptions
{
    public string MyCustomSetting { get; set; }
}

还有一个 local.settings.json 文件,该文件可能按如下所示组织自定义设置:And a local.settings.json file that might structure the custom setting as follows:

{
  "IsEncrypted": false,
  "Values": {
    "MyOptions:MyCustomSetting": "Foobar"
  }
}

Startup.Configure 方法内,可以使用以下代码从 IConfiguration 实例将值提取到自定义类型中:From inside the Startup.Configure method, you can extract values from the IConfiguration instance into your custom type using the following code:

builder.Services.AddOptions<MyOptions>()
    .Configure<IConfiguration>((settings, configuration) =>
    {
        configuration.GetSection("MyOptions").Bind(settings);
    });

调用 Bind 可以将那些与属性名匹配的值从配置复制到自定义实例中。Calling Bind copies values that have matching property names from the configuration into the custom instance. IoC 容器中现在提供可以注入到函数中的选项实例。The options instance is now available in the IoC container to inject into a function.

选项对象作为泛型 IOptions 接口的实例注入函数中。The options object is injected into the function as an instance of the generic IOptions interface. 使用 Value 属性访问在配置中发现的值。Use the Value property to access the values found in your configuration.

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

    public HttpTrigger(IOptions<MyOptions> options)
    {
        _settings = options.Value;
    }
}

有关使用选项的更多详细信息,请参阅 ASP.NET Core 中的选项模式Refer to Options pattern in ASP.NET Core for more details regarding working with options.

自定义配置源Customizing configuration sources

备注

从 Azure Functions 主机版本 2.0.14192.0 和 3.0.14191.0 开始,可以使用配置源自定义。Configuration source customization is available beginning in Azure Functions host versions 2.0.14192.0 and 3.0.14191.0.

若要指定其他配置源,请替代函数应用的 StartUp 类中的 ConfigureAppConfiguration 方法。To specify additional configuration sources, override the ConfigureAppConfiguration method in your function app's StartUp class.

以下示例从基础映像和可选的特定于环境的应用设置文件中添加配置值。The following sample adds configuration values from a base and an optional environment-specific app settings files.

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();
        }
    }
}

将配置提供程序添加到 IFunctionsConfigurationBuilderConfigurationBuilder 属性。Add configuration providers to the ConfigurationBuilder property of IFunctionsConfigurationBuilder. 有关使用配置提供程序的详细信息,请参阅 ASP.NET Core 中的配置For more information on using configuration providers, see Configuration in ASP.NET Core.

FunctionsHostBuilderContext 是从 IFunctionsConfigurationBuilder.GetContext() 中获取的。A FunctionsHostBuilderContext is obtained from IFunctionsConfigurationBuilder.GetContext(). 使用此上下文检索当前环境名称,并解析函数应用文件夹中配置文件的位置。Use this context to retrieve the current environment name and resolve the location of configuration files in your function app folder.

默认情况下,不会自动将配置文件(如 appsettings.json)复制到函数应用的输出文件夹。By default, configuration files such as appsettings.json are not automatically copied to the function app's output folder. 更新 .csproj 文件以匹配以下示例,从而确保文件已复制。Update your .csproj file to match the following sample to ensure the files are copied.

<None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>      
</None>
<None Update="appsettings.Development.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>

重要

对于在消耗计划或高级计划中运行的函数应用,对在触发器中使用的配置值所做的修改可能导致缩放错误。For function apps running in the Consumption or Premium plans, modifications to configuration values used in triggers can cause scaling errors. FunctionsStartup 类对这些属性所做的任何更改都会导致函数应用启动错误。Any changes to these properties by the FunctionsStartup class results in a function app startup error.

后续步骤Next steps

有关详细信息,请参阅以下资源:For more information, see the following resources: