Использование внедрения зависимостей в Функциях Azure .NET

Функции Azure поддерживают шаблон разработки программного обеспечения с внедрением зависимостей (DI), который является приемом инверсии управления (IoC) между классами и их зависимостями.

  • Внедрение зависимостей в Функциях Azure основано на функциях внедрения зависимостей .NET Core. Рекомендуется ознакомиться с Внедрением зависимостей .NET Core. Существуют различия в переопределении зависимостей и считывании значений конфигурации, если служба Функций Azure использует план потребления.

  • Поддержка внедрения зависимостей появилась в Функциях Azure 2.x.

  • Зависимости внедряются по-разному, если C# функция выполняется внутри процесса или вне процесса.

Важно!

Рекомендации в этой статье относятся только к функциям библиотеки классов C#, которые выполняются внутри процесса со средой выполнения. Эта особенная модель внедрения зависимостей не применяется к изолированным функциям .NET, что позволяет запускать функции .NET 5.0 вне процесса. Модель изолированного процесса .NET основана на обычных шаблонах внедрения зависимостей ASP.NET Core. Дополнительные сведения см. в разделе Внедрение зависимостей в руководстве по изолированному процессу .NET.

Предварительные требования

Прежде чем можно будет использовать внедрение зависимостей, необходимо установить следующие пакеты NuGet:

Регистрация служб

Чтобы зарегистрировать службы, создайте метод для настройки и добавления компонентов в экземпляр IFunctionsHostBuilder. Узел функций Azure создает экземпляр IFunctionsHostBuilder и передает его непосредственно в метод.

Чтобы зарегистрировать метод, добавьте атрибут сборки 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>();
        }
    }
}

В этом примере используется пакет Microsoft.Extensions.Http, необходимый для регистрации HttpClient при запуске.

Предупреждения

Последовательность действий по регистрации выполняется до и после того, как среда выполнения обработает класс запуска. Поэтому помните следующие важные моменты.

  • Класс запуска предназначен только для установки и регистрации. Не используйте службы, зарегистрированные при запуске, в процессе запуска. Например, не пытайтесь зарегистрировать сообщение в средстве ведения журнала, регистрируемом во время запуска. Эта точка процесса регистрации слишком ранняя, чтобы службы были доступны для использования. После выполнения метода Configure среда выполнения Функций продолжит регистрировать дополнительные зависимости, что может повлиять на работу служб.

  • Контейнер внедрения зависимостей содержит только явно зарегистрированные типы. Единственными службами, доступными в качестве внедряемых типов, являются те, что настроены в методе Configure. В результате такие относящиеся к Функциям типы, как BindingContext и ExecutionContext, недоступны во время установки или в качестве типов для внедрения.

Использование внедренных зависимостей

Внедрение конструктора используется для обеспечения доступности зависимостей в функции. При внедрении в конструктор требуется, чтобы внедренные службы и классы функций не использовали статические классы.

В следующем примере показано, как зависимости 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 предоставляют те же самые времена существования служб, что и внедрение зависимостей ASP.NET. Для приложения Функций различные времена существования службы ведут себя следующим образом.

  • Временные. Временные службы создаются при каждом разрешении службы.
  • С заданной областью: время существования службы с заданной областью соответствует времени выполнения функции. Службы с заданной областью создаются один раз во время выполнение функции. Последующие запросы к этой службе во время выполнения повторно используют существующий экземпляр службы.
  • Отдельная: время существования отдельной службы соответствует времени существования узла. Она повторно используется при повторном выполнении функций на этом экземпляре. Для подключений и клиентов рекомендуется использовать службы со временем жизни «отдельная», например экземпляры DocumentClient или HttpClient.

Просмотрите или скачайте образцы служб с различными временами существования на GitHub.

Службы ведения журналов

Если вам нужен собственный поставщик средств ведения журнала, зарегистрируйте настраиваемый тип как экземпляр ILoggerProvider, доступный в пакете NuGet Microsoft.Extensions.Logging.Abstractions.

Application Insights автоматически добавляется Функциями Azure.

Предупреждение

  • Не добавляйте AddApplicationInsightsTelemetry() в коллекцию служб, иначе будут зарегистрированы службы, конфликтующие со службами, предоставляемыми средой.
  • Не регистрируйте собственные TelemetryConfiguration или TelemetryClient при использовании встроенных функций Application Insights. Если вам нужно настроить собственный экземпляр TelemetryClient, создайте его с помощью внедренной TelemetryConfiguration, как показано в разделе Запись пользовательских данных телеметрии в функциях C#.

ILogger и 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"
        }
    }
}

Дополнительные сведения об уровнях ведения журнала см. в разделе Настройка уровней ведения журнала.

Службы, предоставляемые приложением-функцией

Узел функции регистрирует множество служб. Следующие службы можно уверенно использовать в качестве зависимости в приложении.

Тип службы Время существования Описание
Microsoft.Extensions.Configuration.IConfiguration Отдельная Конфигурация среды выполнения
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider Отдельная Отвечает за предоставление идентификатора экземпляра узла.

Если существуют другие службы, от которых требуется зависимость, создайте вопрос и предложите его на GitHub.

Переопределение служб узла

Переопределение служб, предоставляемых узлом, в настоящее время не поддерживается. Если имеются службы, которые необходимо переопределить, создайте вопрос и предложите его на GitHub.

Работа с настройками и параметрами

Значения, определенные в параметрах приложения, доступны в экземпляре IConfiguration, который позволяет считывать значения параметров приложения в классе запуска.

Значения можно извлечь из экземпляра IConfiguration в пользовательский тип. Копирование значений параметров приложения в пользовательский тип упрощает тестирование служб, делая эти значения внедряемыми. Параметры, считываемые в экземпляр конфигурации, должны быть простыми парами «ключ-значение».

Рассмотрим следующий класс, который включает свойство, именуемое consistent, с параметром приложения,

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 для внедрения в функцию.

Объект параметров внедряется в функцию как экземпляр универсального интерфейса 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 Core.

Чтобы настроить проект Функций Azure для .NET на использование секретов пользователя, выполните следующую команду в корневом каталоге проекта.

dotnet user-secrets init

Затем используйте команду dotnet user-secrets set для создания или обновления секретов.

dotnet user-secrets set MySecret "my secret value"

Для доступа к секретам пользователя в коде приложения-функции используйте IConfiguration или IOptions.

Настройка источников конфигурации

Примечание

Источник конфигурации можно настроить, начиная с версии узла "Функции Azure" 2.0.14192.0 и 3.0.14191.0.

Чтобы указать дополнительные источники конфигурации, переопределите метод ConfigureAppConfiguration в классе приложения-функции StartUp.

В следующем примере добавляются значения конфигурации из основного и дополнительного файла параметров приложения для конкретной среды.

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

Добавьте поставщиков конфигурации в свойство ConfigurationBuilder у IFunctionsConfigurationBuilder. Дополнительные сведения об использовании поставщиков конфигурации см. в разделе Конфигурация в 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>

Важно!

Для приложений-функций, выполняющихся в планах "Потребление" или "Премиум", изменение значений конфигурации, используемых в триггерах, может привести к ошибкам масштабирования. Любые изменения этих свойств классом FunctionsStartup приводят к ошибке при запуске приложения-функции.

Дальнейшие действия

Для получения дополнительных сведений см. следующие ресурсы: