Verwenden der Abhängigkeitsinjektion in Azure Functions (.NET)

Azure Functions unterstützt das Softwareentwurfsmuster „Abhängigkeitsinjektion“ (Dependency Injection, DI). Damit kann eine Umkehrung der Steuerung (Inversion of Control, IoC) zwischen Klassen und ihren Abhängigkeiten erreicht werden.

  • Die Abhängigkeitsinjektion in Azure Functions basiert auf den .NET Core-Features für die Abhängigkeitsinjektion. Vertrautheit mit der .NET Core- Abhängigkeitsinjektion wird empfohlen. Es bestehen Unterschiede darin, wie Sie Abhängigkeiten überschreiben und wie Konfigurationswerte mit Azure Functions im Verbrauchstarif gelesen werden.

  • Die Unterstützung der Abhängigkeitsinjektion beginnt mit Azure Functions 2.x.

  • Abhängigkeitsinjektionsmuster unterscheiden sich abhängig davon, ob Ihre C#-Funktionen in-Process oder außerhalb des Prozessesausgeführt werden.

Wichtig

Die Anleitung in diesem Artikel gilt nur für Funktionen der C#-Klassenbibliothek, die in-Process mit der Laufzeit ausgeführt werden. Dieses benutzerdefinierte Abhängigkeitsinjektionsmodell gilt nicht für isolierte .NET-Funktionen, mit denen Sie .NET-Funktionen außerhalb des Prozesses ausführen können. Das isolierte .NET-Workerprozessmodell basiert auf regulären Abhängigkeitsinjektionsmustern von ASP.NET Core. Weitere Informationen finden Sie unter Abhängigkeitsinjektion in der Anleitung zum isolierten Workerprozess für .NET.

Voraussetzungen

Bevor Sie die Abhängigkeitsinjektion verwenden können, müssen Sie die folgenden NuGet-Pakete installieren:

Registrieren von Diensten

Um Dienste zu registrieren, erstellen Sie eine Methode zum Konfigurieren und Hinzufügen von Komponenten zu einer IFunctionsHostBuilder-Instanz. Der Azure Functions-Host erstellt eine Instanz von IFunctionsHostBuilder und übergibt diese direkt an Ihre Methode.

Warnung

Bei Funktions-Apps, die im Nutzungs- oder Premium-Plan ausgeführt werden, können Änderungen an Konfigurationswerten, die in Triggern verwendet werden, zu Skalierungsfehlern führen. Alle Änderungen an diesen Eigenschaften durch die FunctionsStartup-Klasse führen zu einem Startfehler der Funktions-App.

Das Einschleusen von IConfiguration kann zu unerwartetem Verhalten führen. Weitere Informationen zum Hinzufügen von Konfigurationsquellen finden Sie unter Anpassen von Konfigurationsquellen.

Fügen Sie zum Registrieren der Methode das FunctionsStartup-Assembly-Attribut hinzu, mit dem der Typname angegeben wird, der beim Starten verwendet wird.

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

In diesem Beispiel wird das Paket Microsoft.Extensions.Http verwendet, das beim Start HttpClient registrieren muss.

Vorbehalte

Eine Reihe von Registrierungsschritten wird vor und nach dem Verarbeiten der Startup-Klasse ausgeführt. Berücksichtigen Sie daher die folgenden Punkte:

  • Die Startup-Klasse ist nur für Setup und Registrierung vorgesehen. Vermeiden Sie die Verwendung von Diensten, die beim Start während des Startvorgangs registriert werden. Versuchen Sie beispielsweise nicht, eine Nachricht in einer Protokollierung zu protokollieren, die während des Starts registriert wird. Dieser Zeitpunkt des Registrierungsprozesses ist zu früh, damit ihre Dienste zur Verwendung verfügbar sind. Nachdem die Configure-Methode ausgeführt wurde, registriert die Azure Functions-Laufzeit weiterhin zusätzliche Abhängigkeiten, die sich auf den Betrieb ihrer Dienste auswirken können.

  • Der Container für die Abhängigkeitsinjektion enthält nur explizit registrierte Typen. Die einzigen Dienste, die als einschleusbare Typen verfügbar sind, sind diejenigen, die in der Configure-Methode eingerichtet werden. Folglich sind Azure Functions-spezifische Typen wie BindingContext und ExecutionContext während des Setups oder als injizierbare Typen nicht verfügbar.

  • Das Konfigurieren der ASP.NET-Authentifizierung wird nicht unterstützt. Der Functions-Host konfiguriert ASP.NET-Authentifizierungsdienste, um APIs ordnungsgemäß für wichtige Lebenszyklusvorgänge verfügbar zu machen. Andere Konfigurationen in einer benutzerdefinierten Startup-Klasse können diese Konfiguration außer Kraft setzen, was unbeabsichtigte Folgen verursacht. Beispielsweise kann das Aufrufen von builder.Services.AddAuthentication() die Authentifizierung zwischen dem Portal und dem Host unterbrechen, was Meldungen wie Azure Functions-Laufzeit ist nicht erreichbar auslöst.

Verwenden von eingefügten Abhängigkeiten

Die Konstruktorinjektion wird verwendet, um Ihre Abhängigkeiten in einer Funktion verfügbar zu machen. Bei Verwendung der Konstruktoreinschleusung dürfen für eingeschleuste Dienste oder für Ihre Funktionsklassen keine statischen Klassen verwendet werden.

Im folgenden Beispiel wird veranschaulicht, wie die Abhängigkeiten IMyService und HttpClient in eine per HTTP ausgelöste Funktion eingefügt werden.

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.");
    }
}

In diesem Beispiel wird das Paket Microsoft.Extensions.Http verwendet, das beim Start HttpClient registrieren muss.

Dienstlebensdauer

Azure Functions-Apps bieten dieselbe Dienstlebensdauer wie die ASP.NET-Abhängigkeitsinjektion. Bei einer Funktions-App weisen die verschiedenen Dienstlebensdauern die folgenden Verhalten auf:

  • Vorübergehend: Bei jeder Lösung des Diensts werden vorübergehende Dienste erstellt.
  • Bereichsbezogen: Die bereichsbezogene Lebensdauer eines Diensts entspricht der Ausführungslebensdauer einer Funktion. Bereichsbezogene Dienste werden einmal pro Funktionsausführung erstellt. In späteren Anforderungen für diesen Dienst während der Ausführung wird die vorhandene Dienstinstanz wiederverwendet.
  • Singleton: Die Lebensdauer eines Singletondiensts entspricht der Lebensdauer des Hosts und wird für Funktionsausführungen dieser Instanz wiederverwendet. Dienste mit Singleton-Lebensdauer werden für Verbindungen und Clients empfohlen, z. B. Instanzen von DocumentClient oder HttpClient.

Sie können auf GitHub ein Beispiel für verschiedene Dienstlebensdauern anzeigen bzw. herunterladen.

Protokollierungsdienste

Wenn Sie einen eigenen Protokollierungsanbieter benötigen, registrieren Sie einen benutzerdefinierten Typ als eine Instanz von ILoggerProvider (verfügbar über das NuGet-Paket Microsoft.Extensions.Logging.Abstractions).

Application Insights wird von Azure Functions automatisch hinzugefügt.

Warnung

  • Fügen Sie AddApplicationInsightsTelemetry(), wodurch Dienste registriert werden, die zu Konflikten mit den von der Umgebung bereitgestellten Diensten führen können, nicht der Dienstsammlung hinzu.
  • Registrieren Sie keine eigene Instanz von TelemetryConfiguration oder TelemetryClient, wenn Sie die integrierten Application Insights-Funktionen verwenden. Wenn Sie Ihre eigene TelemetryClient-Instanz konfigurieren müssen, erstellen Sie diese über die eingefügte TelemetryConfiguration, wie in Protokollieren von benutzerdefinierter Telemetrie in C#-Funktionen gezeigt.

ILogger<T> und ILoggerFactory

Vom Host werden die Dienste ILogger<T> und ILoggerFactory in Konstruktoren eingefügt. Diese neuen Protokollierungsfilter werden jedoch standardmäßig aus den Funktionsprotokollen herausgefiltert. Sie müssen die host.json-Datei ändern, um zusätzliche Filter und Kategorien verwenden zu können.

Im folgenden Beispiel wird ein Element vom Typ ILogger<HttpTrigger> mit Protokollen hinzugefügt, die für den Host verfügbar gemacht werden:

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.");

        // ...
}

In der folgenden Beispieldatei host.json wird der Protokollfilter hinzugefügt:

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

Weitere Informationen zu Protokollgraden finden Sie unter Konfigurieren von Protokollgraden.

Dienste, die von Funktions-App bereitgestellt werden

Der Funktionshost registriert viele Dienste. Es ist sicher, die folgenden Dienste als Abhängigkeit in Ihrer Anwendung zu verwenden:

Diensttyp Gültigkeitsdauer BESCHREIBUNG
Microsoft.Extensions.Configuration.IConfiguration Singleton Laufzeitkonfiguration
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider Singleton Verantwortlich für die Bereitstellung der Hostinstanz-ID

Erstellen Sie ein Issue, und schlagen Sie es auf GitHub vor, wenn Sie eine Abhängigkeit von anderen Diensten erstellen möchten.

Überschreiben von Hostdiensten

Das Überschreiben der vom Host bereitgestellten Dienste wird derzeit nicht unterstützt. Erstellen Sie ein Issue, und schlagen Sie es auf GitHub vor, falls andere Dienste vorhanden sind, die Sie überschreiben möchten.

Arbeiten mit Optionen und Einstellungen

Werte, die in App-Einstellungen definiert sind, sind in einer IConfiguration-Instanz verfügbar, die es Ihnen ermöglicht, App-Einstellungswerte in der Startup-Klasse zu lesen.

Sie können Werte aus der IConfiguration-Instanz in einen benutzerdefinierten Typ extrahieren. Wenn Sie App-Einstellungswerte in einen benutzerdefinierten Typ kopieren, können Sie Ihre Dienste mühelos testen, indem Sie diese Werte injizierbar gestalten. Die in die Konfigurationsinstanz eingelesenen Einstellungen müssen einfache Schlüssel/Wert-Paare sein. Bei Funktionen, die in einem Elastic Premium-Plan ausgeführt werden, dürfen Anwendungseinstellungsnamen nur Buchstaben, Zahlen (0-9), Punkte (.), Doppelpunkte (:) und Unterstriche (_) enthalten. Weitere Informationen finden Sie in den Überlegungen zu App-Einstellungen.

Beachten Sie die folgende Klasse, die eine Eigenschaft mit dem Namen „consistent“ mit einer App-Einstellung enthält:

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

Sowie eine Datei local.settings.json, die die benutzerdefinierte Einstellung möglicherweise wie folgt strukturiert:

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

Innerhalb der Startup.Configure-Methode können Sie Werte aus der IConfiguration-Instanz mit dem folgenden Code in Ihren benutzerdefinierten Typ extrahieren:

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

Durch den Aufruf von Bind werden Werte mit übereinstimmenden Eigenschaftsnamen aus der Konfiguration in die benutzerdefinierte Instanz kopiert. Die Optionsinstanz ist nun im IoC-Container zum Injizieren in eine Funktion verfügbar.

Das Optionsobjekt wird als Instanz der generischen IOptions-Schnittstelle in die Funktion injiziert. Verwenden Sie die Value-Eigenschaft, um auf die in Ihrer Konfiguration gefundenen Werte zuzugreifen.

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

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

Weitere Informationen finden Sie unter Optionsmuster in ASP.NET Core.

Verwenden von geheimen ASP.NET Core-Benutzerschlüsseln

Wenn Sie Ihre App lokal entwickeln, bietet ASP.NET Core das Secret Manager-Tool, mit dem Sie Informationen zu Geheimnissen außerhalb des Projektstamms speichern können. Dadurch sinkt die Wahrscheinlichkeit, dass geheime Schlüssel versehentlich an die Quellcodeverwaltung übertragen werden. Azure Functions Core Tools (Version 3.0.3233 oder höher) liest automatisch geheime Schlüssel, die vom ASP.NET Core Secret Manager erstellt wurden.

Führen Sie den folgenden Befehl im Stammverzeichnis des .NET Azure Functions-Projekts aus, um es für die Verwendung von geheimen Benutzerschlüsseln zu konfigurieren.

dotnet user-secrets init

Verwenden Sie dann den Befehl dotnet user-secrets set, um geheime Schlüssel zu erstellen oder zu aktualisieren.

dotnet user-secrets set MySecret "my secret value"

Verwenden Sie IConfiguration oder IOptions, um auf die Werte der geheimen Benutzerschlüssel in Ihrem Funktions-App-Code zuzugreifen.

Anpassen von Konfigurationsquellen

Überschreiben Sie zum Angeben zusätzlicher Konfigurationsquellen die ConfigureAppConfiguration-Methode in der StartUp-Klasse Ihrer Funktions-App.

Im folgenden Beispiel werden Konfigurationswerte aus einer Basisdatei und einer Datei mit optionalen umgebungsspezifischen App-Einstellungen hinzugefügt.

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

Fügen Sie der Eigenschaft ConfigurationBuilder von IFunctionsConfigurationBuilder Konfigurationsanbieter hinzu. Weitere Informationen zur Verwendung von Konfigurationsanbietern finden Sie unter Konfiguration in ASP.NET Core.

FunctionsHostBuilderContext wird aus IFunctionsConfigurationBuilder.GetContext() abgerufen. Verwenden Sie diesen Kontext, um den aktuellen Umgebungsnamen abzurufen und den Speicherort der Konfigurationsdateien in Ihrem Funktions-App-Ordner aufzulösen.

Standardmäßig werden Konfigurationsdateien wie appsettings.json nicht automatisch in den Ausgabeordner der Funktions-App kopiert. Aktualisieren Sie Ihre .csproj-Datei dem folgenden Beispiel entsprechend, um sicherzustellen, dass die Dateien kopiert werden.

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

Nächste Schritte

Weitere Informationen finden Sie in den folgenden Ressourcen: