C#-Skriptentwicklerreferenz (C#-Skript, CSX) zu Azure Functions

Dieser Artikel ist eine Einführung in die Entwicklung von Azure Functions mithilfe von C#-Skript (CSX).

Azure Functions unterstützt die Programmiersprachen C# und C#-Skript. Wenn Sie Anleitungen zum Verwenden von C# in einem Visual Studio-Klassenbibliotheksprojekt suchen, sollten Sie zur C#-Entwicklerreferenz wechseln.

In diesem Artikel wird davon ausgegangen, dass Sie das Azure Functions: Entwicklerhandbuch bereits gelesen haben.

Funktionsweise von CSX

Die C#-Skriptoberfläche für Azure Functions basiert auf dem Azure WebJobs SDK. Daten fließen über Methodenargumente in Ihre C#-Funktion ein. Argumentnamen werden in einer function.json-Datei angegeben, und es gibt vordefinierte Namen für den Zugriff auf Elemente wie die Funktionsprotokollierung und Abbruchtoken.

Dank des CSX-Formats müssen Sie weniger Textbausteine schreiben und können sich ganz auf das Schreiben einer C#-Funktion konzentrieren. Anstatt sämtliche Informationen in einem Namespace und einer Klasse zu umschließen, definieren Sie einfach eine Run-Methode. Schließen Sie wie gewohnt alle Assemblyverweise und Namespaces am Anfang der Datei ein.

Die CSX-Dateien einer Funktions-App werden kompiliert, wenn eine Instanz initialisiert wird. Dieser Kompilierungsschritt bedeutet, dass bestimmte Dinge, etwa ein Kaltstart, für C#-Skriptfunktionen im Vergleich zu C#-Klassenbibliotheken länger dauern. Dieser Kompilierungsschritt ist auch der Grund, warum C#-Skriptfunktionen im Azure-Portal bearbeitet werden können, während dies für C#-Klassenbibliotheken nicht möglich ist.

Ordnerstruktur

Die Ordnerstruktur für ein C#-Skriptprojekt sieht wie folgt aus:

FunctionsProject
 | - MyFirstFunction
 | | - run.csx
 | | - function.json
 | | - function.proj
 | - MySecondFunction
 | | - run.csx
 | | - function.json
 | | - function.proj
 | - host.json
 | - extensions.csproj
 | - bin

Sie können die freigegebene Datei host.json zum Konfigurieren der Funktions-App verwenden. Jede Funktion verfügt über eine eigene Codedatei (CSX-Datei) sowie über eine eigene Bindungskonfigurationsdatei (function.json).

Die in Version 2.x oder höher der Functions-Runtime erforderlichen Bindungserweiterungen sind in der Datei extensions.csproj definiert, die eigentlichen Bibliotheksdateien befinden sich im Ordner bin. Wenn Sie lokal entwickeln, müssen Sie Bindungserweiterungen registrieren. Wenn Sie Funktionen im Azure-Portal entwickeln, wird diese Registrierung für Sie ausgeführt.

Binden an Argumente

Eingabe- oder Ausgabedaten werden über die name-Eigenschaft in der function.json-Konfigurationsdatei an dienen C#-Skriptfunktionsparameter gebunden. Im folgenden Beispiel werden eine function.json- und eine run.csx-Datei für eine über die Warteschlange ausgelöste Funktion gezeigt. Der Parameter, der die Daten aus der Warteschlangennachricht empfängt, hat den Namen myQueueItem, weil dies der Wert der name-Eigenschaft ist.

{
    "disabled": false,
    "bindings": [
        {
            "type": "queueTrigger",
            "direction": "in",
            "name": "myQueueItem",
            "queueName": "myqueue-items",
            "connection":"MyStorageConnectionAppSetting"
        }
    ]
}
#r "Microsoft.WindowsAzure.Storage"

using Microsoft.Extensions.Logging;
using Microsoft.WindowsAzure.Storage.Queue;
using System;

public static void Run(CloudQueueMessage myQueueItem, ILogger log)
{
    log.LogInformation($"C# Queue trigger function processed: {myQueueItem.AsString}");
}

Die #r-Anweisung ist weiter unten in diesem Artikel erläutert.

Unterstützte Typen für Bindungen

Jede Bindung hat ihre eigenen unterstützten Typen. Beispielsweise kann ein Blobtrigger mit einem Zeichenfolgeparameter, einem POCO-Parameter, einem CloudBlockBlob-Parameter oder einem von mehreren anderen unterstützten Typen verwendet werden. Im Bindungsreferenzartikel für Blobbindungen sind alle unterstützten Parametertypen für Blobtrigger aufgelistet. Weitere Informationen hierzu finden Sie unter Trigger und Bindungen und in den Bindungsreferenzdokumenten für jeden Bindungstyp.

Tipp

Wenn Sie die HTTP- oder WebHook-Bindungen verwenden möchten, vermeiden Sie die Portauslastung, die durch nicht ordnungsgemäße Instanziierung von HttpClient verursacht werden kann. Weitere Informationen finden Sie unter How to manage connections in Azure Functions (Verwalten von Verbindungen in Azure Functions).

Verweisen auf benutzerdefinierte Klassen

Wenn Sie eine benutzerdefinierte POCO-Klasse (Plain Old CLR Object) verwenden müssen, können Sie die Klassendefinition in dieselbe Datei einfügen oder in eine separate Datei setzen.

Im folgenden Beispiel wird ein run.csx-Beispiel veranschaulicht, das eine POCO-Klassendefinition enthält.

public static void Run(string myBlob, out MyClass myQueueItem)
{
    log.Verbose($"C# Blob trigger function processed: {myBlob}");
    myQueueItem = new MyClass() { Id = "myid" };
}

public class MyClass
{
    public string Id { get; set; }
}

Bei einer POCO-Klasse müssen für jede Eigenschaft ein Getter und ein Setter definiert sein.

Wiederverwenden von CSX-Code

Sie können in der Datei run.csx Klassen und Methoden verwenden, die in anderen CSX -Dateien definiert sind. Verwenden Sie zu diesem Zweck #load-Anweisungen in der Datei run.csx. Im folgenden Beispiel wird die Protokollierungsroutine MyLogger in myLogger.csx freigegeben und mit der #load-Anweisung in run.csx geladen:

Beispiel für run.csx:

#load "mylogger.csx"

using Microsoft.Extensions.Logging;

public static void Run(TimerInfo myTimer, ILogger log)
{
    log.LogInformation($"Log by run.csx: {DateTime.Now}");
    MyLogger(log, $"Log by MyLogger: {DateTime.Now}");
}

Beispiel für mylogger.csx:

public static void MyLogger(ILogger log, string logtext)
{
    log.LogInformation(logtext);
}

Die Verwendung einer freigegebenen CSX-Datei ist ein häufiges Verfahren, wenn die Daten, die über ein POCO-Objekt zwischen Funktionen übertragen werden, stark typisiert werden sollen. Im folgenden vereinfachten Beispiel verwenden ein HTTP-Trigger und ein Warteschlangentrigger gemeinsam das POCO-Objekt Order, um die Bestelldaten stark zu typisieren:

Beispiel: run.csx für HTTP-Trigger:

#load "..\shared\order.csx"

using System.Net;
using Microsoft.Extensions.Logging;

public static async Task<HttpResponseMessage> Run(Order req, IAsyncCollector<Order> outputQueueItem, ILogger log)
{
    log.LogInformation("C# HTTP trigger function received an order.");
    log.LogInformation(req.ToString());
    log.LogInformation("Submitting to processing queue.");

    if (req.orderId == null)
    {
        return new HttpResponseMessage(HttpStatusCode.BadRequest);
    }
    else
    {
        await outputQueueItem.AddAsync(req);
        return new HttpResponseMessage(HttpStatusCode.OK);
    }
}

Beispiel: run.csx für Warteschlangentrigger:

#load "..\shared\order.csx"

using System;
using Microsoft.Extensions.Logging;

public static void Run(Order myQueueItem, out Order outputQueueItem, ILogger log)
{
    log.LogInformation($"C# Queue trigger function processed order...");
    log.LogInformation(myQueueItem.ToString());

    outputQueueItem = myQueueItem;
}

Beispiel: order.csx:

public class Order
{
    public string orderId {get; set; }
    public string custName {get; set;}
    public string custAddress {get; set;}
    public string custEmail {get; set;}
    public string cartId {get; set; }

    public override String ToString()
    {
        return "\n{\n\torderId : " + orderId +
                  "\n\tcustName : " + custName +
                  "\n\tcustAddress : " + custAddress +
                  "\n\tcustEmail : " + custEmail +
                  "\n\tcartId : " + cartId + "\n}";
    }
}

Mit der #load -Direktive können Sie einen relativen Pfad verwenden:

  • #load "mylogger.csx" : Lädt eine Datei, die sich im Funktionsordner befindet.
  • #load "loadedfiles\mylogger.csx" : Lädt eine Datei, die sich in einem Ordner im Funktionsordner befindet.
  • #load "..\shared\mylogger.csx" : Lädt eine Datei, die sich in einem Ordner auf der gleichen Ebene befindet wie der Funktionsordner (also direkt unter wwwroot).

Die #load-Direktive kann nur mit CSX-Dateien verwendet werden, nicht mit CS-Dateien.

Binden an den Rückgabewert einer Methode

Sie können für eine Ausgabebindung den Rückgabewert einer Methode verwenden, indem Sie den Namen $return in der Datei function.json verwenden. Beispiele finden Sie unter Konzepte für Azure Functions-Trigger und -Bindungen.

Verwenden Sie den Rückgabewert nur dann, wenn eine erfolgreiche Ausführung der Funktion immer einen Rückgabewert ergibt, der an die Ausgabebindung übergeben werden soll. Verwenden Sie andernfalls, wie im folgenden Abschnitt gezeigt, ICollector oder IAsyncCollector.

Schreiben von mehreren Ausgabewerten

Verwenden Sie die Typen ICollector oder IAsyncCollector in folgenden Fällen: 1. um mehrere Werte in eine Ausgabebindung zu schreiben oder 2. wenn ein erfolgreicher Funktionsaufruf möglicherweise keinen übergebbaren Wert für die Ausgabebindung ergibt. Diese Typen stellen lesegeschützte Sammlungen dar, die nach Durchführung der Methode in die Ausgabebindung geschrieben werden.

In diesem Beispiel werden mehrere Warteschlangennachrichten mit ICollector in die gleiche Warteschlange geschrieben:

public static void Run(ICollector<string> myQueue, ILogger log)
{
    myQueue.Add("Hello");
    myQueue.Add("World!");
}

Protokollierung

Um eine Ausgabe in C# in Ihren Streamingprotokollen zu dokumentieren, fügen Sie ein Argument vom Typ ILogger ein. Verwenden Sie hierzu am besten den Namen log. Vermeiden Sie die Verwendung von Console.Write in Azure Functions.

public static void Run(string myBlob, ILogger log)
{
    log.LogInformation($"C# Blob trigger function processed: {myBlob}");
}

Hinweis

Informationen zu einem neueren Protokollierungsframework, das Sie anstelle von TraceWriter verwenden können, finden Sie in der ILogger-Dokumentation im Entwicklerleitfaden für .NET-Klassenbibliotheken.

Protokollieren von benutzerdefinierten Metriken

Sie können die LogMetric-Erweiterungsmethode in ILogger verwenden, um in Application Insights benutzerdefinierte Metriken zu erstellen. Hier ist ein Beispiel für einen Methodenaufruf angegeben:

logger.LogMetric("TestMetric", 1234);

Dieser Code ist eine Alternative zum Aufrufen von TrackMetric mithilfe der Application Insights-API für .NET.

Async

Um eine Funktion asynchron auszuführen, verwenden Sie das -Schlüsselwort, und geben Sie ein Task-Objekt zurück.

public async static Task ProcessQueueMessageAsync(
        string blobName,
        Stream blobInput,
        Stream blobOutput)
{
    await blobInput.CopyToAsync(blobOutput, 4096);
}

Sie können keine out-Parameter in asynchronen Funktionen verwenden. Für Ausgabebindungen verwenden Sie stattdessen Funktionsrückgabewert oder Sammlerobjekt.

Abbruchtoken

Eine Funktion kann einen CancellationToken-Parameter annehmen, der es dem Betriebssystem ermöglicht, den Code vor dem Beenden der Funktion zu benachrichtigen. Sie können diese Benachrichtigung verwenden, um sicherzustellen, dass die Funktion nicht auf eine Weise unerwartet beendet wird, die die Daten in einem inkonsistenten Zustand hinterlässt.

Das folgende Beispiel zeigt, wie Sie nach einer bevorstehenden Beendigung einer Funktion suchen.

using System;
using System.IO;
using System.Threading;

public static void Run(
    string inputText,
    TextWriter logger,
    CancellationToken token)
{
    for (int i = 0; i < 100; i++)
    {
        if (token.IsCancellationRequested)
        {
            logger.WriteLine("Function was cancelled at iteration {0}", i);
            break;
        }
        Thread.Sleep(5000);
        logger.WriteLine("Normal processing for queue message={0}", inputText);
    }
}

Importieren von Namespaces

Wenn Sie Namespaces importieren müssen, ist dies wie üblich über die using -Klausel möglich.

using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger log)

Die folgenden Namespaces werden automatisch importiert und sind daher optional:

  • System
  • System.Collections.Generic
  • System.IO
  • System.Linq
  • System.Net.Http
  • System.Threading.Tasks
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host

Verweise auf externe Assemblys

Bei Frameworkassemblys können Sie Verweise über die #r "AssemblyName" -Anweisung hinzufügen.

#r "System.Web.Http"

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public static Task<HttpResponseMessage> Run(HttpRequestMessage req, ILogger log)

Die folgenden Assemblys werden automatisch von der Azure Functions-Hostumgebung hinzugefügt:

  • mscorlib
  • System
  • System.Core
  • System.Xml
  • System.Net.Http
  • Microsoft.Azure.WebJobs
  • Microsoft.Azure.WebJobs.Host
  • Microsoft.Azure.WebJobs.Extensions
  • System.Web.Http
  • System.Net.Http.Formatting

Von einem einfachen Namen kann auf folgende Assemblys verwiesen (z.B. #r "AssemblyName"):

  • Newtonsoft.Json
  • Microsoft.WindowsAzure.Storage
  • Microsoft.ServiceBus
  • Microsoft.AspNet.WebHooks.Receivers
  • Microsoft.AspNet.WebHooks.Common
  • Microsoft.Azure.NotificationHubs

Verweisen von benutzerdefinierten Assemblys

Um eine benutzerdefinierte Assembly zu verweisen, können Sie entweder eine freigegebene Assembly oder eine private Assembly verwenden:

  • Freigegebene Assemblys werden für alle Funktionen innerhalb einer Funktionen-App freigegeben. Um auf eine benutzerdefinierte Assembly zu verweisen, laden Sie die Assembly in einen Ordner namens bin im Stammverzeichnis der Funktions-App (wwwroot) hoch.

  • Private Assemblys sind Bestandteil eines Kontexts einer bestimmten Funktion und unterstützen das Querladen von verschiedenen Versionen. Private Assemblys sollten in einen bin-Ordner im Funktionsverzeichnis hochgeladen werden. Verweisen Sie auf die Assemblys, indem Sie den Dateinamen verwenden, etwa #r "MyAssembly.dll".

Informationen zum Hochladen von Dateien in Ihren Funktionenordner finden Sie im Abschnitt zur Paketverwaltung.

Überwachte Verzeichnisse

Das Verzeichnis, das die Skriptdatei für die Funktion enthält, wird automatisch im Hinblick auf Änderungen an Assemblys überwacht. Um Änderungen an Assemblys in anderen Verzeichnissen zu überwachen, fügen Sie sie der Liste watchDirectories in der Datei host.json hinzu.

Verwenden von NuGet-Paketen

Die Art und Weise, wie sowohl Bindungserweiterungspakete als auch andere NuGet-Pakete zu Ihrer Funktions-App hinzugefügt werden, hängt von der zielbezogenen Version der Funktions-Laufzeit ab.

Standardmäßig werden die unterstützten Funktionenerweiterungs-NuGet-Pakete für Ihre C#-Skript-Funktions-App mithilfe von Erweiterungsbündeln zur Verfügung gestellt. Weitere Informationen finden Sie unter Erweiterungspakete.

Wenn Sie aus irgendeinem Grund Erweiterungsbündel in Ihrem Projekt nicht verwenden können, können Sie auch die Azure Functions Core Tools verwenden, um Erweiterungen basierend auf Bindungen zu installieren, die in den function.json-Dateien in Ihrer App definiert sind. Wenn Sie Core Tools verwenden, um Erweiterungen zu registrieren, stellen Sie sicher, dass Sie die Option --csx verwenden. Weitere Informationen finden Sie unter Installieren von Erweiterungen.

Standardmäßig liest Core Tools die function.json-Dateien und fügt die erforderlichen Pakete zu einer extension.csproj-C#-Klassenbibliothek-Projektdatei im Stamm des Dateisystems der Funktions-App (wwwroot) hinzu. Da Core Tools dotnet.exe verwendet, können Sie sie verwenden, um einen beliebigen NuGet-Paketverweis zu dieser Erweiterungsdatei hinzuzufügen. Während der Installation erstellt Core Tools die extensions.csproj, um die erforderlichen Bibliotheken zu installieren. Hier sehen Sie ein Beispiel für die Datei extensions.csproj, die einen Verweis auf Microsoft.ProjectOxford.Face (Version 1.1.0) hinzufügt:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="Microsoft.ProjectOxford.Face" Version="1.1.0" />
    </ItemGroup>
</Project>

Um einen benutzerdefinierten NuGet-Feed zu verwenden, geben Sie den Feed in der Datei Nuget.Config im Stammordner der Funktions-App an. Weitere Informationen finden Sie unter Konfigurieren des NuGet-Verhaltens.

Wenn Sie nur im Portal an Ihrem Projekt arbeiten, müssen Sie die Datei extensions.csproj oder eine Nuget.Config-Datei direkt auf der Website manuell erstellen. Weitere Informationen finden Sie unter Manuelles Installieren von Erweiterungen.

Umgebungsvariablen

Verwenden Sie System.Environment.GetEnvironmentVariablezum Abrufen einer Umgebungsvariablen oder zum Abrufen des Werts einer App-Einstellung, wie im folgenden Codebeispiel zu sehen:

public static void Run(TimerInfo myTimer, ILogger log)
{
    log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
    log.LogInformation(GetEnvironmentVariable("AzureWebJobsStorage"));
    log.LogInformation(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
}

public static string GetEnvironmentVariable(string name)
{
    return name + ": " +
        System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}

Binden zur Laufzeit

In C# und anderen .NET-Sprachen können Sie ein imperatives Bindungsmuster verwenden, im Gegensatz zu den deklarativen Bindungen in function.json. Imperative Bindung eignet sich, wenn Bindungsparameter zur Laufzeit statt zur Entwurfszeit berechnet werden müssen. Mit diesem Muster ist die Bindung an unterstützte Eingabe- und Ausgabebindungen direkt im Funktionscode möglich.

Definieren Sie eine imperative Bindung wie folgt:

  • Schließen Sie für die gewünschten imperativen Bindungen keinen Eintrag in function.json ein.
  • Übergeben Sie den Eingabeparameter Binder binder oder IBinder binder.
  • Verwenden Sie das folgende C#-Muster, um die Datenbindung auszuführen.
using (var output = await binder.BindAsync<T>(new BindingTypeAttribute(...)))
{
    ...
}

BindingTypeAttribute ist das .NET-Attribut, das die Bindung definiert, und T ist ein Eingabe- oder Ausgabetyp, der von diesem Bindungstyp unterstützt wird. T darf kein out-Parametertyp sein (wie etwa out JObject). Die ausgehende Bindung der Mobile Apps-Tabelle unterstützt z. B. sechs Ausgabetypen. Sie können jedoch nur ICollector<T> oder IAsyncCollector<T> für T verwenden.

Beispiel mit einem einzigen Attribut

Mit dem folgenden Beispielcode wird eine ausgehende Speicherblob-Bindung mit einem Blobpfad erstellt, der zur Laufzeit definiert wird. Dann wird eine Zeichenfolge in das Blob geschrieben.

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    using (var writer = await binder.BindAsync<TextWriter>(new BlobAttribute("samples-output/path")))
    {
        writer.Write("Hello World!!");
    }
}

BlobAttribute definiert die Eingabe- oder Ausgabebindung für den Speicherblob, und TextWriter ist ein unterstützter Ausgabenbindungstyp.

Beispiel mit mehreren Attributen

Im vorherigen Beispiel wird die App-Einstellung für die Verbindungszeichenfolge (AzureWebJobsStorage) des Hauptspeicherkontos der Funktions-App abgerufen. Sie können eine benutzerdefinierte App-Einstellung angeben, die für das Storage-Konto verwendet werden soll, indem Sie StorageAccountAttribute hinzufügen und das Attributarray an übergeben. Verwenden Sie einen Binder-Parameter, nicht IBinder. Beispiel:

using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings.Runtime;

public static async Task Run(string input, Binder binder)
{
    var attributes = new Attribute[]
    {
        new BlobAttribute("samples-output/path"),
        new StorageAccountAttribute("MyStorageAccount")
    };

    using (var writer = await binder.BindAsync<TextWriter>(attributes))
    {
        writer.Write("Hello World!");
    }
}

In der folgenden Tabelle sind die .NET-Attribute für jeden Bindungstyp und die Pakete aufgelistet, in denen sie definiert sind.

Bindung attribute Hinzuzufügender Verweis
Cosmos DB Microsoft.Azure.WebJobs.DocumentDBAttribute #r "Microsoft.Azure.WebJobs.Extensions.CosmosDB"
Event Hubs Microsoft.Azure.WebJobs.ServiceBus.EventHubAttribute, Microsoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.Jobs.ServiceBus"
Mobile Apps Microsoft.Azure.WebJobs.MobileTableAttribute #r "Microsoft.Azure.WebJobs.Extensions.MobileApps"
Notification Hubs Microsoft.Azure.WebJobs.NotificationHubAttribute #r "Microsoft.Azure.WebJobs.Extensions.NotificationHubs"
Service Bus Microsoft.Azure.WebJobs.ServiceBusAttribute, Microsoft.Azure.WebJobs.ServiceBusAccountAttribute #r "Microsoft.Azure.WebJobs.ServiceBus"
Speicherwarteschlange Microsoft.Azure.WebJobs.QueueAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
Speicherblob Microsoft.Azure.WebJobs.BlobAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
Speichertabelle Microsoft.Azure.WebJobs.TableAttribute, Microsoft.Azure.WebJobs.StorageAccountAttribute
Twilio Microsoft.Azure.WebJobs.TwilioSmsAttribute #r "Microsoft.Azure.WebJobs.Extensions.Twilio"

Nächste Schritte