Lambda-Funktions-Handler in C# - AWS Lambda

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Lambda-Funktions-Handler in C#

Der Lambda-Funktionshandler ist die Methode in Ihrem Funktionscode, die Ereignisse verarbeitet. Wenn Ihre Funktion aufgerufen wird, führt Lambda die Handler-Methode aus. Ihre Funktion wird so lange ausgeführt, bis der Handler eine Antwort zurückgibt, beendet wird oder ein Timeout auftritt.

Wenn Ihre Funktion aufgerufen wird und Lambda die Handler-Methode Ihrer Funktion ausführt, übergibt es zwei Argumente an Ihre Funktion. Das erste Argument ist das event-Objekt. Wenn ein anderes Ihre Funktion AWS-Service aufruft, enthält das event Objekt Daten über das Ereignis, das dazu geführt hat, dass Ihre Funktion aufgerufen wurde. Ein event-Objekt von API Gateway enthält beispielsweise Informationen über den Pfad, die HTTP-Methode und HTTP-Header. Die genaue Ereignisstruktur variiert je nach AWS-Service Aufruf Ihrer Funktion. Weitere Informationen zu Veranstaltungsformaten für einzelne Dienste finden Sie unter Verwendung AWS Lambda mit anderen Diensten.

Lambda übergibt auch ein context-Objekt an Ihre Funktion. Dieses Objekt enthält Informationen über den Aufruf, die Funktion und die Ausführungsumgebung. Weitere Informationen finden Sie unter AWS Lambda-Context-Objekt in C#.

Das native Format für alle Lambda-Ereignisse sind Bytestreams, die das Ereignis im JSON-Format darstellen. Wenn Ihre Funktionseingabe- und Ausgabeparameter nicht vom Typ System.IO.Stream sind, müssen Sie sie serialisieren. Geben Sie den zu verwendenden Serialisierer an, indem Sie das Assembly-Attribut LambdaSerializer setzen. Weitere Informationen finden Sie unter Serialisieren von Lambda-Funktionen.

.NET-Ausführungsmodelle für Lambda

Es gibt zwei verschiedene Ausführungsmodelle für die Ausführung von Lambda-Funktionen in .NET: den Klassenbibliotheksansatz und den Ansatz für ausführbare Assemblys.

Beim Ansatz der Klassenbibliothek übermitteln Sie Lambda eine Zeichenfolge mit den Angaben AssemblyName, ClassName, und Method der aufzurufenden Funktion. Weitere Informationen über das Format dieser Zeichenfolge finden Sie unter Handler für Klassenbibliotheken. Während der Initialisierungsphase der Funktion wird die Klasse Ihrer Funktion initialisiert und der gesamte Code im Konstruktor wird ausgeführt.

Beim Ansatz für ausführbare Assemblys verwenden Sie das Feature für Anweisungen der obersten Ebene von C# 9. Dieser Ansatz generiert eine ausführbare Assembly, die Lambda immer dann ausführt, wenn sie einen Aufrufbefehl für Ihre Funktion empfängt. Sie geben Lambda nur den Namen der ausführbaren Assembly an, die ausgeführt werden soll.

Die folgenden Abschnitte enthalten Beispielfunktionscode für diese beiden Ansätze.

Handler für Klassenbibliotheken

Der folgende Lambda-Funktionscode zeigt ein Beispiel für eine Handler-Methode (FunctionHandler) für eine Lambda-Funktion, die den Klassenbibliotheksansatz verwendet. In dieser Beispielfunktion empfängt Lambda ein Ereignis von API Gateway, das die Funktion aufruft. Die Funktion liest einen Datensatz aus einer Datenbank und gibt den Datensatz als Teil der API-Gateway-Antwort zurück.

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; } }

Wenn Sie eine Lambda-Funktion erstellen, müssen Sie Lambda Informationen über den Handler Ihrer Funktion in Form eines Handler-Strings zur Verfügung stellen. Dadurch wird Lambda mitgeteilt, welche Methode in Ihrem Code ausgeführt werden soll, wenn Ihre Funktion aufgerufen wird. In C# lautet das Format der Handlerzeichenfolge bei Verwendung des Klassenbibliotheksansatzes wie folgt:

ASSEMBLY::TYPE::METHOD, wobei:

  • ASSEMBLY ist der Name der .NET-Assembly-Datei für Ihre Anwendung. Wenn Sie die Amazon.Lambda.Tools CLI zur Erstellung Ihrer Anwendung verwenden und den Assembly-Namen nicht über die Eigenschaft AssemblyName in der .csproj-Datei festlegen, ist ASSEMBLY einfach der Name Ihrer .csproj-Datei.

  • TYPE ist der vollständige Name des Handler-Typs, der aus Namespace und ClassName besteht.

  • METHOD ist der Name der Funktionshandlermethode in Ihrem Code.

Wenn die Baugruppe im Beispielcode den Namen GetProductHandler trägt, lautet die Zeichenfolge für den Handler GetProductHandler::GetProductHandler.Function::FunctionHandler.

Ausführbare Assembly-Handler

Im folgenden Beispiel ist die Lambda-Funktion als ausführbare Assembly definiert. Die Handler-Methode in diesem Code heißt Handler. Bei der Verwendung ausführbarer Assemblys muss die Lambda-Laufzeit gebootet werden. Dazu verwenden Sie die LambdaBootstrapBuilder.Create-Methode. Diese Methode nimmt als Eingaben die Methode, die Ihre Funktion als Handler verwendet, und den zu verwendenden Lambda-Serializer entgegen.

Weitere Informationen zur Verwendung von Anweisungen der obersten Ebene finden Sie unter Einführung in die .NET-6-Laufzeit für AWS Lambda im - AWS Compute-Blog.

namespace GetProductHandler; IDatabaseRepository repo = new DatabaseRepository(); await LambdaBootstrapBuilder.Create<APIGatewayProxyRequest>(Handler, new DefaultLambdaJsonSerializer()) .Build() .RunAsync(); async Task<APIGatewayProxyResponse> Handler(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) { var id = input.PathParameters["id"]; var databaseRecord = await this.repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord) }; };

Bei der Verwendung von ausführbaren Assemblys ist der Handler-String, der Lambda mitteilt, wie Ihr Code ausgeführt werden soll, der Name der Assembly. In diesem Beispiel wäre das GetProductHandler.

Serialisieren von Lambda-Funktionen

Wenn Ihre Lambda-Funktion andere Eingabe- oder Ausgabetypen als ein Stream-Objekt verwendet, müssen Sie eine Serialisierungsbibliothek zu Ihrer Anwendung hinzufügen. Sie können die Serialisierung entweder mit der standardmäßigen reflexionsbasierten Serialisierung implementieren, die von System.Text.Json und Newtonsoft.Json bereitgestellt wird, oder mit der quellgenerierten Serialisierung.

Verwenden Sie die durch die Quelle generierte Serialisierung

Die quellgenerierte Serialisierung ist ein Feature der .NET-Versionen 6 und höher, mit der Serialisierungscode zur Kompilierzeit generiert werden kann. Sie macht Reflexionen überflüssig und kann die Leistung Ihrer Funktion verbessern. Gehen Sie wie folgt vor, um die quellgenerierte Serialisierung in Ihrer Funktion zu verwenden:

  • Erstellen Sie eine neue Teilklasse, die von JsonSerializerContext erbt, und fügen Sie JsonSerializable-Attribute für alle Typen hinzu, die serialisiert oder deserialisiert werden müssen.

  • Konfigurieren Sie das LambdaSerializer so, dass es ein SourceGeneratorLambdaJsonSerializer<T> verwendet.

  • Aktualisieren Sie alle manuellen Serialisierungen oder Deserialisierungen in Ihrem Anwendungscode, um die neu erstellte Klasse zu verwenden.

Eine Beispielfunktion, die quellgenerierte Serialisierung verwendet, wird im folgenden Code gezeigt.

[assembly: LambdaSerializer(typeof(SourceGeneratorLambdaJsonSerializer<CustomSerializer>))] public class Function { private readonly IDatabaseRepository _repo; public Function() { this._repo = new DatabaseRepository(); } public async Task<APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request) { var id = request.PathParameters["id"]; var databaseRecord = await this._repo.GetById(id); return new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = JsonSerializer.Serialize(databaseRecord, CustomSerializer.Default.Product) }; } } [JsonSerializable(typeof(APIGatewayProxyRequest))] [JsonSerializable(typeof(APIGatewayProxyResponse))] [JsonSerializable(typeof(Product))] public partial class CustomSerializer : JsonSerializerContext { }
Anmerkung

Wenn Sie die native Ahead-of-Time-Compilation (AOT) mit Lambda verwenden möchten, müssen Sie die quellgenerierte Serialisierung verwenden.

Verwenden Sie die reflektionsbasierte Serialisierung

AWS bietet vorgefertigte Bibliotheken, mit denen Sie Ihrer Anwendung schnell Serialisierung hinzufügen können. Sie konfigurieren dies entweder mit den Amazon.Lambda.Serialization.Json NuGet Paketen Amazon.Lambda.Serialization.SystemTextJson oder . Hinter den Kulissen verwendet Amazon.Lambda.Serialization.SystemTextJson System.Text.Json, um Serialisierungsaufgaben durchzuführen, und Amazon.Lambda.Serialization.Json verwendet das Paket Newtonsoft.Json.

Sie können auch Ihre eigene Serialisierungsbibliothek erstellen, indem Sie die ILambdaSerializer-Schnittstelle implementieren; diese ist als Teil der Amazon.Lambda.Core-Bibliothek verfügbar. Diese Schnittstelle definiert zwei Methoden:

  • T Deserialize<T>(Stream requestStream);

    Sie implementieren diese Methode für die Deserialisierung der Anfragenutzlast von der Invoke-API in das Objekt, das Ihrem Lambda-Funktions-Handler übergeben wird.

  • T Serialize<T>(T response, Stream responseStream);

    Sie implementieren diese Methode, um das von Ihrem Lambda-Funktions-Handler zurückgegebene Ergebnis in die Antwort-Nutzlast zu serialisieren, die der Invoke-API-Vorgang zurückgibt.

Vereinfachen Sie den Funktionscode mit dem Lambda Annotations Framework

Lambda Annotations ist ein Framework für .NET 6 und .NET 8, das das Schreiben von Lambda-Funktionen mit C# vereinfacht. Mit dem Annotations-Framework können Sie einen Großteil des Codes in einer Lambda-Funktion ersetzen, die mit dem regulären Programmiermodell geschrieben wurde. Code, der mit dem Framework geschrieben wurde, verwendet einfachere Ausdrücke, sodass Sie sich auf Ihre Geschäftslogik konzentrieren können.

Der folgende Beispielcode zeigt, wie die Verwendung des Annotations-Frameworks das Schreiben von Lambda-Funktionen vereinfachen kann. Das erste Beispiel zeigt Code, der mit dem regulären Lambda-Programmmodell geschrieben wurde, und das zweite zeigt das Äquivalent unter Verwendung des Annotations-Frameworks.

public APIGatewayHttpApiV2ProxyResponse LambdaMathAdd(APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) { if (!request.PathParameters.TryGetValue("x", out var xs)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } if (!request.PathParameters.TryGetValue("y", out var ys)) { return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }; } var x = int.Parse(xs); var y = int.Parse(ys); return new APIGatewayHttpApiV2ProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = (x + y).ToString(), Headers = new Dictionary≪string, string> { { "Content-Type", "text/plain" } } }; }
[LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/add/{x}/{y}")] public int Add(int x, int y) { return x + y; }

Ein weiteres Beispiel dafür, wie die Verwendung von Lambda Annotations Ihren Code vereinfachen kann, finden Sie in dieser serviceübergreifenden Beispielanwendung im -awsdocs/aws-doc-sdk-examples GitHub Repository. Der Ordner PamApiAnnotations verwendet Lambda-Anmerkungen in der function.cs-Hauptdatei. Zum Vergleich: Der PamApi-Ordner enthält äquivalente Dateien, die mit dem regulären Lambda-Programmiermodell geschrieben wurden.

Das Annotations-Framework verwendet Quellgeneratoren, um Code zu generieren, der vom Lambda-Programmiermodell in den Code aus dem zweiten Beispiel übersetzt wird.

Weitere Informationen über die Verwendung von Lambda-Annotationen für .NET finden Sie in den folgenden Ressourcen:

Abhängigkeitsinjektion mit dem Lambda Annotations-Framework

Sie können auch das Lambda Annotations-Framework verwenden, um Ihren Lambda-Funktionen eine Dependency Injection hinzuzufügen, indem Sie die Syntax verwenden, mit der Sie vertraut sind. Wenn Sie einer [LambdaStartup]-Datei ein Startup.cs-Attribut hinzufügen, generiert das Lambda Annotations-Framework den erforderlichen Code zur Kompilierzeit.

[LambdaStartup] public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IDatabaseRepository, DatabaseRepository>(); } }

Ihre Lambda-Funktion kann Dienste entweder durch Konstruktorinjektion oder durch Injektion in einzelne Methoden unter Verwendung des [FromServices]-Attributs injizieren.

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] namespace GetProductHandler; public class Function { private readonly IDatabaseRepository _repo; public Function(IDatabaseRepository repo) { this._repo = repo; } [LambdaFunction] [HttpApi(LambdaHttpMethod.Get, "/product/{id}")] public async Task<Product> FunctionHandler([FromServices] IDatabaseRepository repository, string id) { return await this._repo.GetById(id); } }

Einschränkungen des Lambda-Funktionshandlers

Beachten Sie, dass für die Handlersignatur einige Einschränkungen bestehen.

  • Möglicherweise ist es nicht unsafe, Pointer-Typen in der Handler-Signatur zu verwenden, Sie können jedoch unsafe-Kontext in der Handler-Methode und ihren Abhängigkeiten verwenden. Weitere Informationen finden Sie unter unsafe (C#-Referenz) auf der Microsoft-Docs-Website.

  • Eine variable Anzahl von Parametern kann nicht mit dem Schlüsselwort params weitergegeben werden und ArgIterator kann nicht als Eingabe- oder Rückgabe-Parameter verwendet werden, der zur Unterstützung einer variablen Anzahl von Parametern verwendet wird.

  • Der Handler darf keine generische Methode sein (z. B. IList<T> Sort<T>(IList<T> input)).

  • Asynchrone Handler mit der Signatur async void werden nicht unterstützt.