Generazione di codice Orleans

Prima di Orleans 7.0, la generazione di origine era molto più manuale e richiedeva l'intervento esplicito dello sviluppatore. A partire da Orleans 7.0, la generazione del codice è automatica e non richiede alcun intervento da parte dello sviluppatore. Tuttavia, esistono ancora casi in cui gli sviluppatori possono voler influenzare la generazione del codice, ad esempio per generare codice per i tipi che non vengono generati automaticamente o per generare codice per i tipi in un altro assembly.

Abilitare la generazione di codice

Orleans genera il codice sorgente C# per l'app in fase di compilazione. Tutti i progetti, incluso l'host, devono avere installati i pacchetti NuGet appropriati per abilitare la generazione del codice. Sono disponibili i pacchetti seguenti:

Utilizzare l’oggetto GenerateSerializerAttribute per specificare che il tipo deve essere serializzato e che il codice di serializzazione deve essere generato per il tipo. Per altre informazioni, vedere Usare la serializzazione Orleans.

Il runtime Orleans usa il codice generato per garantire una corretta serializzazione dei tipi usati nel cluster, nonché per generare boilerplate, che astrae i dettagli di implementazione del metodo shipping, la propagazione delle eccezioni e altri concetti di runtime interno. La generazione di codice può essere eseguita quando i progetti vengono compilati o quando l'applicazione viene inizializzata.

Cosa accade durante la compilazione?

In fase di compilazione, Orleans genera il codice per tutti i tipi contrassegnati con GenerateSerializerAttribute. Se un tipo non è contrassegnato con GenerateSerializer, non verrà serializzato da Orleans.

Se si sviluppa con F# o Visual Basic, è anche possibile usare la generazione di codice. Per altre informazioni, vedere gli esempi seguenti:

Questi esempi illustrano come utilizzare Orleans.GenerateCodeForDeclaringAssemblyAttribute, specificando i tipi nell'assembly per cui il generatore di origine deve esaminare e generare l'origine.

Il metodo preferito per l'esecuzione della generazione di codice è in fase di compilazione. La generazione di codice in fase di compilazione può essere abilitata usando uno dei pacchetti seguenti:

  • Microsoft.Orleans.OrleansCodeGenerator.Build. Pacchetto che usa Roslyn per la generazione di codice e usa reflection .NET per l'analisi.
  • Microsoft.Orleans.CodeGenerator.MSBuild. Nuovo pacchetto di generazione di codice che sfrutta Roslyn sia per la generazione del codice che per l'analisi del codice. Non carica i file binari dell'applicazione e, di conseguenza, evita problemi causati da conflitti tra versioni delle dipendenze e framework di destinazione diversi. Il nuovo generatore di codice migliora anche il supporto per le compilazioni incrementali, il che dovrebbe comportare tempi di compilazione più brevi.

Uno di questi pacchetti deve essere installato in tutti i progetti che contengono grani, interfacce di granularità, serializzatori personalizzati o tipi inviati tra grani. L'installazione di un pacchetto inserisce una destinazione nel progetto che genererà il codice in fase di compilazione.

Entrambi i pacchetti (Microsoft.Orleans.CodeGenerator.MSBuild e Microsoft.Orleans.OrleansCodeGenerator.Build) supportano solo i progetti C#. Altri linguaggi sono supportati usando il pacchetto Microsoft.Orleans.OrleansCodeGenerator descritto di seguito o creando un progetto C# che può fungere da destinazione per il codice generato da assembly scritti in altri linguaggi.

È possibile generare diagnostica aggiuntiva in fase di compilazione specificando un valore per OrleansCodeGenLogLevel nel file csproj del progetto di destinazione. Ad esempio: <OrleansCodeGenLogLevel>Trace</OrleansCodeGenLogLevel>.

Cosa accade durante l'inizializzazione?

In Orleans 7+, non accade nulla durante l'inizializzazione. La generazione del codice viene eseguita in fase di compilazione.

La generazione del codice può essere eseguita durante l'inizializzazione nel client e nel silo installando il pacchetto Microsoft.Orleans.OrleansCodeGenerator e usando il metodo di estensione ApplicationPartManagerCodeGenExtensions.WithCodeGeneration:

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

Nell'esempio precedente, builder può essere un'istanza di ISiloHostBuilder o IClientBuilder. È possibile passare un'istanza facoltativa ILoggerFactory a WithCodeGeneration per abilitare la registrazione durante la generazione del codice, ad esempio:

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

Influenzare la generazione di codice

Quando si applica GenerateSerializerAttribute a un tipo, è anche possibile applicare IdAttribute per identificare in modo univoco il membro. Analogamente, è anche possibile applicare un alias con AliasAttribute. Per altre informazioni sull'influenza della generazione del codice, vedere Usare la serializzazione Orleans.

Durante la generazione di codice, è possibile influenzare la generazione di codice per un tipo specifico. Il codice viene generato automaticamente per interfacce granulari, classi granulari, stato granulare e tipi passati come argomenti nei metodi granulari. Se un tipo non soddisfa questi criteri, è possibile usare i metodi seguenti per guidare ulteriormente la generazione di codice.

L'aggiunta di SerializableAttribute a un tipo indica al generatore di codice di generare un serializzatore per tale tipo.

L'aggiunta di [assembly: GenerateSerializer(Type)] a un progetto indica al generatore di codice di considerare tale tipo come serializzabile e genererà un errore se non è stato possibile generare un serializzatore per tale tipo, ad esempio perché il tipo non è accessibile. Questo errore interromperà una compilazione se la generazione di codice è abilitata. Questo attributo consente anche di generare codice per tipi specifici da un altro assembly.

[assembly: KnownType(Type)] indica inoltre al generatore di codice di includere un tipo specifico (che può provenire da un assembly a cui si fa riferimento), ma non causa un'eccezione se il tipo non è accessibile.

Generare serializzatori per tutti i sottotipi

L'aggiunta di KnownBaseTypeAttribute a un'interfaccia o a una classe indica al generatore di codice di generare codice di serializzazione per tutti i tipi che ereditano/implementano tale tipo.

Generare codice per tutti i tipi in un altro assembly

In alcuni casi il codice generato non può essere incluso in un assembly specifico in fase di compilazione. Ad esempio, può includere librerie condivise che non fanno riferimento a Orleans, assembly scritti in linguaggi diversi da C# e assembly in cui lo sviluppatore non dispone del codice sorgente. In questi casi, il codice generato per tali assembly può essere inserito in un assembly separato a cui viene fatto riferimento durante l'inizializzazione.

Per abilitare questa impostazione per un assembly:

  1. Creare un progetto C#.
  2. Installare Microsoft.Orleans.CodeGenerator.MSBuild o il pacchetto Microsoft.Orleans.OrleansCodeGenerator.Build.
  3. Aggiungere un riferimento all'assembly di destinazione.
  4. Aggiungere [assembly: KnownAssembly("OtherAssembly")] al livello superiore di un file C#.

KnownAssemblyAttribute indica al generatore di codice di esaminare l'assembly specificato e generare il codice per i tipi al suo interno. L'attributo può essere usato più volte all'interno di un progetto.

L'assembly generato deve quindi essere aggiunto al client/silo durante l'inizializzazione:

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

Nell'esempio precedente, builder può essere un'istanza di ISiloHostBuilder o IClientBuilder.

KnownAssemblyAttribute dispone di una proprietà facoltativa, TreatTypesAsSerializable, che può essere impostata su true per indicare al generatore di codice di agire come se tutti i tipi all'interno dell'assembly siano contrassegnati come serializzabili.