Orleans 代码生成

在 Orleans 7.0 之前,源代码生成更大程度上依赖手动操作,需要开发人员显式干预。 从 Orleans 7.0 开始,代码生成是自动的,无需开发人员干预。 但是,在某些情况下,开发人员可能希望影响代码生成,例如,为非自动生成的类型的生成代码,或者为另一个程序集中的类型生成代码。

启用代码生成

Orleans 在生成时为应用生成 C# 源代码。 所有项目(包括主机)都需要安装相应的 NuGet 包才能启用代码生成。 可以使用以下程序包:

使用 GenerateSerializerAttribute 指定类型应进行序列化,并且应为该类型生成序列化代码。 有关详细信息,请参阅使用 Orleans 序列化

Orleans 运行时使用生成的代码来确保在整个群集中使用的类型正确序列化,以及生成样板来抽象掉方法传送、异常传播和其他内部运行时概念的实现细节。 可以在项目正在生成时或者在应用程序初始化时执行代码生成。

生成期间会发生什么情况?

在生成时,Orleans 会为标记有 GenerateSerializerAttribute 的所有类型生成代码。 如果类型未标记有 GenerateSerializer,将不被 Orleans 序列化。

如果使用 F# 或 Visual Basic 进行开发,也可以使用代码生成。 有关详细信息,请参阅以下示例:

这些示例演示了如何使用 Orleans.GenerateCodeForDeclaringAssemblyAttribute,指定程序集中源生成器应检查并为其生成源代码的类型。

执行代码生成的首选方法是在生成时执行。 可使用以下包之一启用生成时代码生成:

  • Microsoft.Orleans.OrleansCodeGenerator.Build。 该包使用 Roslyn 进行代码生成,使用 .NET Reflection 进行分析。
  • Microsoft.Orleans.CodeGenerator.MSBuild。 一个新的代码生成包,它利用 Roslyn 进行代码生成和代码分析。 它不加载应用程序二进制文件,因此避免了因依赖项版本冲突和目标框架不同而造成的问题。 新的代码生成器还改进了对增量生成的支持,这应该可以缩短生成时间。

应将其中一个包安装到所有包含 grain、grain 接口、自定义序列化程序或在 grain 之间发送的类型的项目中。 安装某个包会将目标注入到项目中,从而在生成时生成代码。

这两个包(Microsoft.Orleans.CodeGenerator.MSBuildMicrosoft.Orleans.OrleansCodeGenerator.Build)都仅支持 C# 项目。 使用下面所述的 Microsoft.Orleans.OrleansCodeGenerator 包或通过创建一个 C# 项目来支持其他语言,该项目可以充当从用其他语言编写的程序集生成的代码的目标。

通过在目标项目的 .csproj 文件中指定 OrleansCodeGenLogLevel 的值,可以在生成时发出其他诊断信息。 例如,<OrleansCodeGenLogLevel>Trace</OrleansCodeGenLogLevel>

初始化期间会发生什么情况?

在 Orleans 7+ 中,初始化期间什么也没发生。 代码生成在生成时执行。

在初始化期间,可以通过安装 Microsoft.Orleans.OrleansCodeGenerator 包并使用 ApplicationPartManagerCodeGenExtensions.WithCodeGeneration 扩展方法在客户端和 silo 上执行代码生成:

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

在上面的示例中,builder 可以是 ISiloHostBuilderIClientBuilder 的实例。 可将可选的 ILoggerFactory 实例传递给 WithCodeGeneration,以在代码生成期间启用日志记录,例如:

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

影响代码生成

GenerateSerializerAttribute 应用于类型时,还可以应用 IdAttribute 来唯一标识成员。 同样,还可以使用 AliasAttribute 应用别名。 有关影响代码生成的详细信息,请参阅使用 Orleans 序列化

在代码生成期间,可以影响针对特定类型生成代码的行为。 将为在 grain 方法中作为参数传递的 grain 接口、grain 类、grain 状态和类型自动生成代码。 如果某个类型不符合这些条件,可使用以下方法来进一步引导代码生成。

SerializableAttribute 添加到某个类型会指示代码生成器为该类型生成序列化程序。

[assembly: GenerateSerializer(Type)] 添加到某个项目会指示代码生成器将该类型视为可序列化类型,如果无法为该类型生成序列化程序(例如,因为该类型不可访问),则会导致错误。 如果启用了代码生成,此错误将导致生成停止。 此属性还允许从另一个程序集为特定的类型生成代码。

[assembly: KnownType(Type)] 还会指示代码生成器包含特定的类型(可来自被引用程序集),但如果该类型不可访问,则不会导致异常。

为所有子类型生成序列化程序

KnownBaseTypeAttribute 添加到某个接口或类会指示代码生成器为继承/实现该类型的所有类型生成序列化代码。

为另一个程序集中的所有类型生成代码

在某些情况下,生成的代码在生成时无法包含在特定的程序集中。 例如,这可能包括不引用 Orleans 的共享库、用除 C# 以外的语言编写的程序集,以及开发人员在其中没有源代码的程序集。 在这种情况下,为这些程序集生成的代码可以放入在初始化期间引用的独立程序集中。

若要为程序集启用此功能,请执行以下操作:

  1. 创建一个 C# 项目。
  2. 安装 Microsoft.Orleans.CodeGenerator.MSBuildMicrosoft.Orleans.OrleansCodeGenerator.Build 包。
  3. 添加对目标程序集的引用。
  4. 在 C# 文件的顶层添加 [assembly: KnownAssembly("OtherAssembly")]

KnownAssemblyAttribute 指示代码生成器检查指定的程序集,并为其中的类型生成代码。 可以在一个项目中多次使用该属性。

然后,必须在初始化期间将生成的程序集添加到客户端/silo:

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

在上面的示例中,builder 可以是 ISiloHostBuilderIClientBuilder 的实例。

KnownAssemblyAttribute 有一个可选属性 TreatTypesAsSerializable,可将其设置为 true,以指示代码生成器如同该程序集中的所有类型都标记为可序列化时那样进行处理。