Code generation

What the generated C# client contains, the generator block, build-time MSBuild generation, the CI drift check, and the descriptor version handshake.

Generation emits plain C# source into a folder you choose. Unlike the TypeScript client, which is generated into node_modules, the C# client is regular source that compiles through your project's default glob. Commit it or gitignore it, your choice. Serialization, retries, and transport live in the Repost.Client runtime package; the generated code is data and types only.

The generator block

A C# generator block sets three things the other languages don't need:

generator sdk {
  language   = "csharp"
  output     = "../Generated"
  namespace  = "Contoso.Events"
  clientName = "RepostClient"
}
FieldWhat it owns
outputThe source root, resolved relative to the schema file. Repost owns this tree: it replaces its own files atomically and never touches anything else.
namespaceThe C# namespace for the generated models, the Webhooks tree, and the client.
clientNameThe generated client class name (RepostClient above). Pick a distinct name per schema when one project hosts more than one.

What gets emitted

FilePurpose
Client.csThe client class and the Webhooks method tree, plus one input class per event (for example BookCreatedInput).
Models.csModel classes and enums. Optional fields use Optional<T>; enums become C# enums.
Descriptors.csThe serialization descriptor set handed to the runtime at construction, declaring the descriptor-format version this package was emitted for.
schema.repostA verbatim copy of the schema this client was generated from.
schemas.jsonThe JSON Schema catalog for your event types.
.repost-client.jsonThe generation marker: format version, engine version, schema hash, and the file manifest.

Models become classes, enums become C# enums, and optional fields use the tri-state Optional<T> container so absent, explicit null, and present are never conflated:

public enum Currency
{
    Usd,
    Eur,
}
 
public sealed partial class Author
{
    public string Name { get; set; } = null!;
 
    public Optional<string> Email { get; set; }
}

The classes are partial, so you can add your own members in a separate file without touching the generated code.

Build-time generation with MSBuild

For teams that would rather not commit generated source, the Repost.Client.MSBuild package runs the schema engine as an MSBuild task before compilation, the Grpc.Tools idiom:

<PackageReference Include="Repost.Client" Version="1.0.8" />
<PackageReference Include="Repost.Client.MSBuild" Version="1.0.8" PrivateAssets="all" />

With a schema at repost/schema.repost, a normal dotnet build validates the schema, writes the client to Generated/, and compiles it. A second build with an unchanged schema and engine is a pure incremental skip, so design-time builds in Visual Studio no-op instantly. The package carries signed, platform-specific engine binaries and checksum-verifies the resolved binary before every run.

Regenerating and the CI drift check

The repost CLI generates the identical tree from the same schema, so a teammate without the MSBuild package can regenerate too:

repost schema generate

repost schema migrate dev also regenerates as part of recording a migration, and repost schema generate --check verifies in CI that nothing has drifted without writing to your source tree.

The version handshake

The generated Descriptors.cs declares descriptor format version 2, checked by the client constructor before any send:

  • Generated code older than the runtime: the error tells you to re-run repost schema generate with the current engine.
  • Generated code newer than the runtime: the error tells you to upgrade Repost.Client.

A real mismatch throws RepostDescriptorVersionException at construction, never silently at send time.

Continue