Utilizar a injeção de dependências nas Funções do Azure do .NET

O Azure Functions dá suporte ao padrão de design de software de injeção de dependência (DI), que é uma técnica para obter Inversão de Controle (IoC) entre classes e suas dependências.

  • A injeção de dependência no Azure Functions é baseada nos recursos de injeção de dependência do .NET Core. Recomenda-se familiaridade com a injeção de dependência do .NET Core. Há diferenças na forma como você substitui dependências e como os valores de configuração são lidos com o Azure Functions no plano de Consumo.

  • O suporte para injeção de dependência começa com o Azure Functions 2.x.

  • Os padrões de injeção de dependência diferem dependendo se suas funções C# são executadas em processo ou fora do processo.

Importante

A orientação neste artigo se aplica somente às funções da biblioteca de classes C#, que são executadas em processo com o tempo de execução. Esse modelo de injeção de dependência personalizado não se aplica a funções isoladas do .NET, o que permite executar funções do .NET fora do processo. O modelo de processo de trabalho isolado do .NET depende de padrões regulares de injeção de dependência do ASP.NET Core. Para saber mais, consulte Injeção de dependência no guia do processo de trabalho isolado do .NET.

Pré-requisitos

Antes de poder usar a injeção de dependência, você deve instalar os seguintes pacotes NuGet:

Serviços de registo

Para registrar serviços, crie um método para configurar e adicionar componentes a uma IFunctionsHostBuilder instância. O host do Azure Functions cria uma instância e IFunctionsHostBuilder a passa diretamente para seu método.

Aviso

Para aplicativos de função executados nos planos Consumo ou Premium, modificações nos valores de configuração usados em gatilhos podem causar erros de dimensionamento. Qualquer alteração nessas propriedades pela classe resulta em um erro de inicialização do FunctionsStartup aplicativo de função.

A injeção de IConfiguration pode levar a um comportamento inesperado. Para saber mais sobre como adicionar fontes de configuração, consulte Personalizando fontes de configuração.

Para registrar o método, adicione o atributo assembly que especifica o FunctionsStartup nome do tipo usado durante a inicialização.

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

Este exemplo usa o pacote Microsoft.Extensions.Http necessário para registrar uma HttpClient inicialização at.

Limitações

Uma série de etapas de registro são executadas antes e depois que o tempo de execução processa a classe de inicialização. Portanto, tenha em mente os seguintes itens:

  • A classe de inicialização destina-se apenas à configuração e ao registro. Evite usar serviços registrados na inicialização durante o processo de inicialização. Por exemplo, não tente registrar uma mensagem em um registrador que está sendo registrado durante a inicialização. Este ponto do processo de registo é demasiado cedo para que os seus serviços estejam disponíveis para utilização. Depois que o método é executado, o Configure tempo de execução do Functions continua a registrar outras dependências, o que pode afetar como seus serviços operam.

  • O contêiner de injeção de dependência contém apenas tipos explicitamente registrados. Os únicos serviços disponíveis como tipos injetáveis são os configurados no Configure método. Como resultado, tipos específicos de funções como e ExecutionContext não estão disponíveis durante a configuração ou como BindingContext tipos injetáveis.

  • Não há suporte para a configuração da autenticação ASP.NET. O host Functions configura ASP.NET serviços de autenticação para expor corretamente as APIs para operações principais do ciclo de vida. Outras configurações em uma classe personalizada Startup podem substituir essa configuração, causando consequências indesejadas. Por exemplo, a chamada builder.Services.AddAuthentication() pode interromper a autenticação entre o portal e o host, levando a mensagens como o tempo de execução do Azure Functions inacessível.

Usar dependências injetadas

A injeção do construtor é usada para disponibilizar suas dependências em uma função. O uso da injeção do construtor requer que você não use classes estáticas para serviços injetados ou para suas classes de função.

O exemplo a seguir demonstra como as IMyService dependências e HttpClient são injetadas em uma função acionada por 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.");
    }
}

Este exemplo usa o pacote Microsoft.Extensions.Http necessário para registrar uma HttpClient inicialização at.

Vida útil do serviço

Os aplicativos do Azure Functions fornecem os mesmos tempos de vida de serviço que ASP.NET Injeção de Dependência. Para um aplicativo Functions, os diferentes tempos de vida do serviço se comportam da seguinte maneira:

  • Transitório: Serviços transitórios são criados em cada resolução do serviço.
  • Escopo: O tempo de vida do serviço com escopo corresponde a um tempo de vida de execução de função. Os serviços com escopo são criados uma vez por execução de função. Solicitações posteriores para esse serviço durante a execução reutilizam a instância de serviço existente.
  • Singleton: O tempo de vida do serviço singleton corresponde ao tempo de vida do host e é reutilizado em execuções de função nessa instância. Os serviços vitalícios Singleton são recomendados para conexões e clientes, por exemplo DocumentClient , ou HttpClient instâncias.

Veja ou baixe uma amostra de diferentes tempos de vida de serviço no GitHub.

Serviço de registo

Se você precisar de seu próprio provedor de log, registre um tipo personalizado como uma instância do , que está disponível por meio do ILoggerProviderpacote NuGet Microsoft.Extensions.Logging.Abstractions.

O Application Insights é adicionado pelo Azure Functions automaticamente.

Aviso

  • Não adicione AddApplicationInsightsTelemetry() à coleção de serviços, que registra serviços que entram em conflito com os serviços fornecidos pelo ambiente.
  • Não registre o seu próprio TelemetryConfiguration ou TelemetryClient se você estiver usando a funcionalidade interna do Application Insights. Se você precisar configurar sua própria TelemetryClient instância, crie uma por meio da injetada TelemetryConfiguration , conforme mostrado em Telemetria personalizada de log em funções C#.

ILogger T> e ILoggerFactory<

O host injeta e ILoggerFactory presta serviços em construtoresILogger<T>. No entanto, por padrão, esses novos filtros de log são filtrados para fora dos logs de funções. Você precisa modificar o host.json arquivo para optar por filtros e categorias extras.

O exemplo a seguir demonstra como adicionar um ILogger<HttpTrigger> com logs que são expostos ao 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.");

        // ...
}

O arquivo de exemplo host.json a seguir adiciona o filtro de log.

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

Para obter mais informações sobre níveis de log, consulte Configurar níveis de log.

Serviços fornecidos pelo aplicativo de função

O host da função registra muitos serviços. Os seguintes serviços são seguros para serem tomados como uma dependência em seu aplicativo:

Tipo de Serviço Vitalício Description
Microsoft.Extensions.Configuration.IConfiguration Singleton Configuração de tempo de execução
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider Singleton Responsável por fornecer o ID da instância do host

Se houver outros serviços dos quais você deseja depender, crie um problema e proponha-os no GitHub.

Substituindo serviços de host

Atualmente, não há suporte para serviços de substituição fornecidos pelo host. Se houver serviços que você deseja substituir, crie um problema e proponha-os no GitHub.

Trabalhar com opções e definições

Os valores definidos nas configurações do aplicativo estão disponíveis em uma IConfiguration instância, o que permite que você leia os valores das configurações do aplicativo na classe de inicialização.

Você pode extrair valores da IConfiguration instância em um tipo personalizado. Copiar os valores das configurações do aplicativo para um tipo personalizado facilita o teste de seus serviços, tornando esses valores injetáveis. As configurações lidas na instância de configuração devem ser pares chave/valor simples. Para funções executadas em um plano Elastic Premium, os nomes de configuração do aplicativo só podem conter letras, números (), pontos (), dois pontos () e sublinhados (0-9:._). Para obter mais informações, consulte Considerações sobre a configuração do aplicativo.

Considere a seguinte classe que inclui uma propriedade nomeada consistente com uma configuração de aplicativo:

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

E um local.settings.json arquivo que pode estruturar a configuração personalizada da seguinte maneira:

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

De dentro do Startup.Configure método, você pode extrair valores da instância para seu IConfiguration tipo personalizado usando o seguinte código:

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

A chamada Bind copia valores que têm nomes de propriedade correspondentes da configuração para a instância personalizada. A instância de opções agora está disponível no contêiner IoC para injetar em uma função.

O objeto options é injetado na função como uma instância da interface genérica IOptions . Use a Value propriedade para acessar os valores encontrados em sua configuração.

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

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

Para obter mais informações, consulte Padrão de opções no ASP.NET Core.

Usando segredos de usuário do ASP.NET Core

Quando você desenvolve seu aplicativo localmente, o ASP.NET Core fornece uma ferramenta Secret Manager que permite armazenar informações secretas fora da raiz do projeto. Isso torna menos provável que os segredos sejam acidentalmente comprometidos com o controle da fonte. As Ferramentas Principais do Azure Functions (versão 3.0.3233 ou posterior) lêem automaticamente os segredos criados pelo ASP.NET Core Secret Manager.

Para configurar um projeto do .NET Azure Functions para usar segredos de usuário, execute o seguinte comando na raiz do projeto.

dotnet user-secrets init

Em seguida, use o dotnet user-secrets set comando para criar ou atualizar segredos.

dotnet user-secrets set MySecret "my secret value"

Para acessar valores de segredos de usuário no código do aplicativo de função, use IConfiguration ou IOptions.

Personalizando fontes de configuração

Para especificar outras fontes de configuração, substitua o ConfigureAppConfiguration método na classe do seu aplicativo de StartUp função.

O exemplo a seguir adiciona valores de configuração de arquivos de configurações de aplicativos básicos e opcionais específicos do ambiente.

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)
    {
    }
}

Adicione provedores de configuração à ConfigurationBuilder propriedade de IFunctionsConfigurationBuilder. Para obter mais informações sobre como usar provedores de configuração, consulte Configuração no ASP.NET Core.

A FunctionsHostBuilderContext é obtido a partir de IFunctionsConfigurationBuilder.GetContext(). Use esse contexto para recuperar o nome do ambiente atual e resolver o local dos arquivos de configuração na pasta do aplicativo de função.

Por padrão, os arquivos de configuração, como appsettings.json não são copiados automaticamente para a pasta de saída do aplicativo de função. Atualize o .csproj arquivo para corresponder ao exemplo a seguir para garantir que os arquivos sejam copiados.

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

Próximos passos

Para obter mais informações, consulte os seguintes recursos: