Usare l'inserimento delle dipendenze in Funzioni di Azure .NET

Funzioni di Azure supporta lo schema progettuale di software per l'inserimento di dipendenze, una tecnica per ottenere l'IoC (Inversion of Control) tra le classi e le relative dipendenze.

  • L'inserimento di dipendenze in Funzioni di Azure si basa sulle funzionalità di inserimento dipendenze .NET Core. È consigliabile acquisire familiarità con l'inserimento di dipendenze .NET Core. Esistono differenze nel modo in cui viene eseguito l'override delle dipendenze e nel modo in cui i valori di configurazione vengono letti con Funzioni di Azure nel piano a consumo.

  • Il supporto per l'inserimento di dipendenze inizia a partire da Funzioni di Azure 2.x.

  • I modelli di inserimento delle dipendenze variano a seconda che le funzioni C# vengano eseguite in-process o out-of-process.

Importante

Le indicazioni contenute in questo articolo si applicano solo alle funzioni della libreria di classi C#, eseguite in-process con il runtime. Questo modello personalizzato di inserimento delle dipendenze non si applica alle funzioni isolate .NET, che consente di eseguire funzioni .NET out-of-process. Il modello di processo di lavoro isolato .NET si basa sui normali modelli di inserimento delle dipendenze di ASP.NET Core. Per altre informazioni, vedere Inserimento delle dipendenze nella guida al processo di lavoro isolato .NET.

Prerequisiti

Prima di poter usare l'inserimento di dipendenze, è necessario installare i pacchetti NuGet seguenti:

Registrare i servizi

Per registrare i servizi, creare un metodo per configurare e aggiungere componenti a un'istanza di IFunctionsHostBuilder. L'host di Funzioni di Azure crea un'istanza di IFunctionsHostBuilder e la passa direttamente al metodo.

Avviso

Per le app per le funzioni in esecuzione nei piani a consumo o Premium, le modifiche ai valori di configurazione usati nei trigger possono causare errori di ridimensionamento. Tutte le modifiche apportate a queste proprietà dalla classe generano un errore di avvio dell'app per le FunctionsStartup funzioni.

L'inserimento di IConfiguration può causare comportamenti imprevisti. Per altre informazioni sull'aggiunta di origini di configurazione, vedere Personalizzazione delle origini di configurazione.

Per registrare il metodo, aggiungere l'attributo dell'assembly FunctionsStartup che specifica il nome del tipo usato durante l'avvio.

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

Questo esempio usa il pacchetto Microsoft.Extensions.Http necessario per registrare un HttpClient all'avvio.

Precisazioni

Una serie di passaggi di registrazione viene eseguita prima e dopo l'elaborazione della classe di avvio da parte del runtime. Tenere dunque presenti questi elementi:

  • La classe di avvio è destinata solo alla configurazione e alla registrazione. Evitare di usare i servizi registrati all'avvio durante il processo di avvio. Ad esempio, non provare a registrare un messaggio in un logger registrato durante l'avvio. A questo punto del processo di registrazione è troppo presto perché i servizi siano disponibili per l'uso. Dopo l'esecuzione del Configure metodo, il runtime di Funzioni continua a registrare altre dipendenze, che possono influire sul funzionamento dei servizi.

  • Il contenitore di inserimento delle dipendenze include solo tipi registrati in modo esplicito. Gli unici servizi disponibili come tipi iniettabili sono gli elementi configurati nel Configure metodo . Di conseguenza, i tipi specifici per Funzioni come BindingContext e ExecutionContext non sono disponibili durante l'installazione o come tipi inseribili.

  • La configurazione dell'autenticazione ASP.NET non è supportata. L'host funzioni configura ASP.NET servizi di autenticazione per esporre correttamente le API per le operazioni principali del ciclo di vita. Altre configurazioni in una classe personalizzata Startup possono eseguire l'override di questa configurazione, causando conseguenze impreviste. Ad esempio, la chiamata builder.Services.AddAuthentication() può interrompere l'autenticazione tra il portale e l'host, causando messaggi come Funzioni di Azure runtime non raggiungibile.

Usare dipendenze inserite

L'inserimento del costruttore viene usato per rendere disponibili le dipendenze in una funzione. L'uso dell'inserimento del costruttore richiede che non si usino classi statiche per i servizi inseriti o per le classi di funzioni.

Nell'esempio seguente viene illustrata la modalità di inserimento delle dipendenze IMyService e HttpClient in una funzione attivata tramite 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.");
    }
}

Questo esempio usa il pacchetto Microsoft.Extensions.Http necessario per registrare un HttpClient all'avvio.

Durate del servizio

Le app di Funzioni di Azure offrono la stessa durata del servizio dell'inserimento di dipendenze ASP.NET. Per un'app per le funzioni, le diverse durate del servizio si comportano nel modo seguente:

  • Temporaneo: i servizi temporanei vengono creati in ogni risoluzione del servizio.
  • Ambito: la durata del servizio con ambito corrisponde a una durata di esecuzione della funzione. I servizi con ambito vengono creati una volta per ogni esecuzione di funzione. Le richieste successive per un dato servizio durante l'esecuzione riutilizzeranno l'istanza del servizio esistente.
  • Singleton: la durata del servizio singleton corrisponde alla durata dell'host e viene riutilizzata tra le esecuzioni di funzioni in tale istanza. I servizi con durata singleton sono consigliati per connessioni e client, ad esempio per le istanze DocumentClient o HttpClient.

Visualizzare o scaricare un esempio di diverse durate dei servizi su GitHub.

Servizi di registrazione

Se è necessario un provider di registrazione personalizzato, registrare un tipo personalizzato come istanza di ILoggerProvider, disponibile tramite il pacchetto NuGet Microsoft.Extensions.Logging.Abstractions .

Application Insights viene aggiunto automaticamente da Funzioni di Azure.

Avviso

  • Non aggiungere AddApplicationInsightsTelemetry() alla raccolta di servizi, che registra i servizi in conflitto con i servizi forniti dall'ambiente.
  • Non registrare il proprio TelemetryConfiguration o TelemetryClient se si usa la funzionalità predefinita di Application Insights. Se è necessario configurare la propria TelemetryClient istanza, crearne una tramite l'inserimento TelemetryConfiguration come illustrato in Registrare dati di telemetria personalizzati nelle funzioni C#.

ILogger<T> e ILoggerFactory

L'host inserisce ILogger<T> e ILoggerFactory servizi nei costruttori. Tuttavia, per impostazione predefinita, questi nuovi filtri di registrazione vengono filtrati dai log delle funzioni. È necessario modificare il host.json file per acconsentire esplicitamente a filtri e categorie aggiuntivi.

Nell'esempio seguente viene illustrato come aggiungere un oggetto ILogger<HttpTrigger> con i log esposti all'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.");

        // ...
}

Il file di esempio host.json seguente aggiunge il filtro di log.

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

Per altre informazioni sui livelli di log, vedere Configurare i livelli di log.

Servizi forniti dall'app per le funzioni

L'host funzione registra molti servizi. I servizi seguenti possono essere considerati sicuri come una dipendenza nell'applicazione:

Tipo di servizio Durata Descrizione
Microsoft.Extensions.Configuration.IConfiguration Singleton Configurazione del runtime
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider Singleton Responsabile della fornitura dell'ID dell'istanza host

Se sono presenti altri servizi su cui si vuole creare una dipendenza, creare un argomento e proporlo in GitHub.

Override dei servizi host

L'override dei servizi forniti dall'host non è attualmente supportato. Se sono presenti servizi di cui si vuole eseguire l'override, creare un argomento e proporlo in GitHub.

Usare opzioni e impostazioni

I valori definiti nelle impostazioni app sono disponibili in un'istanza di IConfiguration, che consente di leggere i valori delle impostazioni dell'app nella classe di avvio.

È possibile estrarre i valori dall'istanza di IConfiguration in un tipo personalizzato. Copiare i valori delle impostazioni dell'app su un tipo personalizzato facilita il test dei servizi, permettendo l'inserimento di questi valori. Le impostazioni lette nell'istanza di configurazione devono essere semplici coppie chiave/valore. Per le funzioni in esecuzione in un piano Elastic Premium, i nomi delle impostazioni dell'applicazione possono contenere solo lettere, numeri (0-9), punti (.), punti (:) e caratteri di sottolineatura (_). Per altre informazioni, vedere Considerazioni sull'impostazione dell'app.

Si consideri la classe seguente, che include una proprietà denominata in modo coerente con un'impostazione dell'app:

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

E un file local.settings.json, in grado di strutturare l'impostazione personalizzata come indicato di seguito:

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

Dall'interno del metodo Startup.Configure, è possibile estrarre i valori dall'istanza IConfiguration nel tipo personalizzato usando il codice seguente:

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

La chiamata di Bind copia i valori che hanno nomi di proprietà corrispondenti dalla configurazione all'istanza personalizzata. L'istanza Options è ora disponibile nel contenitore IoC per essere inserita in una funzione.

L'oggetto Options viene inserito nella funzione come istanza dell'interfaccia IOptions generica. Usare la proprietà Value per accedere ai valori presenti nella configurazione.

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

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

Per altre informazioni, vedere Modello di opzioni in ASP.NET Core.

Uso dei segreti utente di ASP.NET Core

Quando si sviluppa l'app in locale, ASP.NET Core fornisce uno strumento Secret Manager che consente di archiviare informazioni segrete all'esterno della radice del progetto. Rende meno probabile che i segreti vengano accidentalmente sottoposti a commit nel controllo del codice sorgente. Funzioni di Azure Core Tools (versione 3.0.3233 o successiva) legge automaticamente i segreti creati da ASP.NET Core Secret Manager.

Per configurare un progetto di Funzioni di Azure .NET per l'uso dei segreti utente, eseguire il comando seguente nella radice del progetto.

dotnet user-secrets init

Usare quindi il dotnet user-secrets set comando per creare o aggiornare i segreti.

dotnet user-secrets set MySecret "my secret value"

Per accedere ai valori dei segreti utente nel codice dell'app per le funzioni, usare IConfiguration o IOptions.

Personalizzazione delle origini di configurazione

Per specificare altre origini di configurazione, eseguire l'override del ConfigureAppConfiguration metodo nella classe dell'app per StartUp le funzioni.

L'esempio seguente aggiunge valori di configurazione sia dai file di impostazioni dell'app di base che da quello facoltativo specifico dell'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)
    {
    }
}

Aggiungere provider di configurazione alla ConfigurationBuilder proprietà di IFunctionsConfigurationBuilder. Per altre informazioni sull'uso dei provider di configurazione, vedere Configurazione in ASP.NET Core.

Un FunctionsHostBuilderContext oggetto viene ottenuto da IFunctionsConfigurationBuilder.GetContext(). Usare questo contesto per recuperare il nome dell'ambiente corrente e risolvere il percorso dei file di configurazione nella cartella dell'app per le funzioni.

Per impostazione predefinita, i file di configurazione, appsettings.json ad esempio, non vengono copiati automaticamente nella cartella di output dell'app per le funzioni. Aggiornare il .csproj file in modo che corrisponda all'esempio seguente per assicurarsi che i file vengano copiati.

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

Passaggi successivi

Per ulteriori informazioni, vedi le seguenti risorse: