Codegenerierung in Orleans

Vor Orleans 7.0 bestand die Quellengenerierung aus sehr viel manueller Arbeit und erforderte explizite Eingriffe durch die Entwickler*innen. Ab Orleans 7.0 erfolgt die Codegenerierung automatisch und erfordert keinen Entwicklereingriff. Es gibt jedoch immer noch Fälle, in denen Entwickler*innen die Codegenerierung beeinflussen möchten, z. B. um Code für Typen zu generieren, die nicht automatisch generiert werden oder die sich in einer anderen Assembly befinden.

Aktivieren der Codegenerierung

Orleans generiert den C#-Quellcode für Ihre App zur Erstellungszeit. Für alle Projekte, einschließlich Ihres Hosts, müssen die entsprechenden NuGet-Pakete installiert sein, um die Codegenerierung zu ermöglichen. Folgende Pakete sind verfügbar:

Sie geben mit dem GenerateSerializerAttribute an, dass der Typ serialisiert werden soll und dass Serialisierungscode für den Typ generiert werden soll. Weitere Informationen finden Sie unter Verwenden der Orleans-Serialisierung.

Die Orleans-Runtime nutzt den generierten Code, um die ordnungsgemäße Serialisierung von Typen zu gewährleisten, die im gesamten Cluster verwendet werden, und um Codebausteine zu generieren, die die Details der Implementierung des Methodenversands, der Weitergabe von Ausnahmen und anderer interner Runtimekonzepte abstrahieren. Die Codegenerierung kann entweder während der Erstellung Ihrer Projekte oder bei Initialisierung Ihrer Anwendung erfolgen.

Was geschieht während des Buildvorgangs?

Orleans generiert zur Erstellungszeit Code für alle Typen, die mit GenerateSerializerAttribute gekennzeichnet sind. Wenn ein Typ nicht mit GenerateSerializer gekennzeichnet ist, wird er nicht von Orleans serialisiert.

Wenn Sie mit F# oder Visual Basic entwickeln, können Sie die Codegenerierung ebenfalls verwenden. Weitere Informationen finden Sie in den folgenden Beispielen:

In diesen Beispielen wird veranschaulicht, wie das Orleans.GenerateCodeForDeclaringAssemblyAttribute verwendet wird, um Typen in der Assembly anzugeben, für die der Quellengenerator die Quelle untersuchen und generieren soll.

Der bevorzugte Zeitpunkt zum Generieren von Code ist zur Buildzeit. Die Codegenerierung zur Buildzeit kann mithilfe eines der folgenden Pakete aktiviert werden:

  • Microsoft.Orleans.OrleansCodeGenerator.Build. Ein Paket, das Roslyn zur Codegenerierung und .NET-Reflexion zur Analyse verwendet.
  • Microsoft.Orleans.CodeGenerator.MSBuild. Ein neues Paket zur Codegenerierung, das Roslyn sowohl zur Codegenerierung als auch zur Codeanalyse nutzt. Es lädt keine Binärdateien von Anwendungen und vermeidet so Probleme, die durch widersprüchliche Abhängigkeitsversionen und unterschiedliche Zielframeworks verursacht werden. Der neue Codegenerator verbessert auch die Unterstützung inkrementeller Builds, was zu kürzeren Buildzeiten führen sollte.

Eines dieser Pakete sollte in allen Projekten installiert werden, die Grains, Grain-Schnittstellen, benutzerdefinierte Serialisierungsmodule oder Typen enthalten, die zwischen Grains gesendet werden. Durch die Installation eines Pakets wird ein Ziel in das Projekt eingefügt, das zur Buildzeit Code generiert.

Beide Pakete (Microsoft.Orleans.CodeGenerator.MSBuild und Microsoft.Orleans.OrleansCodeGenerator.Build) werden nur in C#-Projekten unterstützt. Andere Sprachen werden entweder durch das nachstehend beschriebene Paket Microsoft.Orleans.OrleansCodeGenerator oder durch Erstellen eines C#-Projekts unterstützt, das als Ziel für Code dienen kann, der anhand von in anderen Sprachen geschriebenen Assemblys generiert wird.

Eine zusätzliche Diagnose kann zur Buildzeit ausgegeben werden, indem in der CSPROJ-Datei des Zielprojekts ein Wert für OrleansCodeGenLogLevel angegeben wird. Beispiel: <OrleansCodeGenLogLevel>Trace</OrleansCodeGenLogLevel>.

Was geschieht während der Initialisierung?

In Orleans 7 und höher passiert während der Initialisierung gar nichts. Die Codegenerierung erfolgt zur Erstellungszeit.

Die Codegenerierung kann während der Initialisierung auf dem Client und im Silo erfolgen, indem Sie das Paket Microsoft.Orleans.OrleansCodeGenerator installieren und die Erweiterungsmethode ApplicationPartManagerCodeGenExtensions.WithCodeGeneration verwenden:

builder.ConfigureApplicationParts(
    parts => parts
        .AddApplicationPart(typeof(IRuntimeCodeGenGrain).Assembly)
        .WithCodeGeneration());

Im obigen Beispiel kann builder eine Instanz von ISiloHostBuilder oder IClientBuilder sein. Eine optionale ILoggerFactory-Instanz kann an WithCodeGeneration übergeben werden, um die Protokollierung während der Codegenerierung zu aktivieren. Beispiel:

ILoggerFactory codeGenLoggerFactory = new LoggerFactory();
codeGenLoggerFactory.AddProvider(new ConsoleLoggerProvider());
    builder.ConfigureApplicationParts(
        parts => parts
            .AddApplicationPart(typeof(IRuntimeCodeGenGrain).Assembly)
            .WithCodeGeneration(codeGenLoggerFactory));

Beeinflussen der Codegenerierung

Beim Anwenden von GenerateSerializerAttribute auf einen Typ können Sie auch das IdAttribute anwenden, um den Member eindeutig zu identifizieren. Ebenso können Sie auch einen Alias für das AliasAttribute verwenden. Weitere Informationen zum Beeinflussen der Codegenerierung finden Sie unter Verwenden der Orleans-Serialisierung.

Während der Codegenerierung können Sie das Generieren von Code für einen bestimmten Typ beeinflussen. Code wird automatisch für Schnittstellen, Klassen, Zustand und Typen von Grains generiert, die als Argumente in Grain-Methoden übergeben werden. Wenn ein Typ diese Kriterien nicht erfüllt, kann die Codegenerierung mit den folgenden Methoden beeinflusst werden.

Durch Hinzufügen von SerializableAttribute zu einem Typ wird der Codegenerator angewiesen, ein Serialisierungsmodul für diesen Typ zu generieren.

Durch Hinzufügen von [assembly: GenerateSerializer(Type)] zu einem Projekt wird der Codegenerator angewiesen, diesen Typ als serialisierbar zu behandeln, und verursacht einen Fehler, wenn ein Serialisierungsmodul für diesen Typ nicht generiert werden konnte, z. B. weil auf den Typ nicht zugegriffen werden kann. Dieser Fehler hält einen Build bei aktivierter Codegenerierung an. Dieses Attribut ermöglicht auch das Generieren von Code für bestimmte Typen aus einer anderen Assembly.

[assembly: KnownType(Type)] weist den Codegenerator auch an, einen bestimmten Typ einzubeziehen (der aus einer Assembly stammen kann, auf die verwiesen wird), löst aber keine Ausnahme aus, wenn auf den Typ nicht zugegriffen werden kann.

Generieren von Serialisierungsmodulen für alle Untertypen

Durch Hinzufügen von KnownBaseTypeAttribute zu einer Schnittstelle oder Klasse wird der Codegenerator angewiesen, Serialisierungscode für alle Typen zu generieren, die diesen Typ erben/implementieren.

Generieren von Code für alle Typen in einer anderen Assembly

Es gibt Fälle, in denen generierter Code zur Buildzeit nicht in eine bestimmte Assembly eingeschlossen werden kann. Dazu gehören beispielsweise freigegebene Bibliotheken, die nicht auf Orleans verweisen, in anderen Sprachen als C# geschriebene Assemblys und Assemblys, bei denen der Entwickler nicht über den Quellcode verfügt. In diesen Fällen kann der generierte Code für diese Assemblys in einer separaten Assembly abgelegt werden, auf die während der Initialisierung verwiesen wird.

So aktivieren Sie dies für eine Assembly

  1. Erstellen Sie ein C#-Projekt.
  2. Installieren Sie das Paket Microsoft.Orleans.CodeGenerator.MSBuild oder Microsoft.Orleans.OrleansCodeGenerator.Build.
  3. Fügen Sie einen Verweis auf die Zielassembly hinzu.
  4. Fügen Sie auf der obersten Ebene einer C#-Datei [assembly: KnownAssembly("OtherAssembly")] hinzu.

KnownAssemblyAttribute weist den Codegenerator an, die angegebene Assembly zu überprüfen und Code für die darin enthaltenen Typen zu generieren. Das Attribut kann innerhalb eines Projekts mehrmals verwendet werden.

Die generierte Assembly muss dann während der Initialisierung dem Client/Silo hinzugefügt werden:

builder.ConfigureApplicationParts(
    parts => parts.AddApplicationPart("CodeGenAssembly"));

Im obigen Beispiel kann builder eine Instanz von ISiloHostBuilder oder IClientBuilder sein.

KnownAssemblyAttribute hat die optionale Eigenschaft TreatTypesAsSerializable, die auf true festgelegt werden kann, um den Codegenerator anzuweisen, sich so zu verhalten, als ob alle Typen innerhalb dieser Assembly als serialisierbar markiert wären.