A source generator that generates C# code from Avro schemas and is compatible with Apache Avro.
$ dotnet add package AvroSourceGenerator![]()
Avro Source Generator is a modern .NET source generator that produces strongly typed C# models from Avro schema files (.avsc).
It’s designed to be fast, incremental, and compatible with Apache Avro.
Generated code takes advantage of modern C# language features, including nullable reference types, init-only and required properties, and partial records or classes — ensuring clean, idiomatic C# output.
[!NOTE]
You can use the generator without an Avro library, but library-specific features (e.g.,ISpecificRecord) won’t be emitted.
Install the package:
dotnet add package AvroSourceGenerator
Mark it as a build-only dependency in your .csproj file:
<PackageReference Include="AvroSourceGenerator"
Version="*"
PrivateAssets="all"
ExcludeAssets="runtime" />
Include your .avsc schema files as AdditionalFiles:
<ItemGroup>
<AdditionalFiles Include="schemas/*.avsc" />
</ItemGroup>
Add a schema file under schemas/, e.g. schemas/user.avsc:
{
"type": "record",
"name": "User",
"namespace": "example",
"fields": [
{ "name": "Name", "type": "string" },
{ "name": "Email", "type": ["null", "string"] },
{ "name": "CreatedAt", "type": { "type": "long", "logicalType": "timestamp-millis" } }
]
}
[!NOTE]
Avro schema files must have the.avscextension.
Build your project — the generator will create C# types matching your schemas.
For the User schema above, the generator produces (abridged):
// <auto-generated/>
#pragma warning disable CS8618, CS8633, CS8714, CS8775, CS8981
#nullable enable
namespace example;
[global::System.CodeDom.Compiler.GeneratedCode("AvroSourceGenerator", "1.0.0.0")]
public partial record User
{
public required string Name { get; init; }
public string? Email { get; init; }
public required global::System.DateTime CreatedAt { get; init; }
}
partial record User : global::Avro.Specific.ISpecificRecord
{
public global::Avro.Schema Schema => s_schema;
private static readonly global::Avro.Schema s_schema = global::Avro.Schema.Parse("""
{
"type": "record",
"name": "User",
"namespace": "example",
"fields": [
{ "name": "Name", "type": "string" },
{ "name": "Email", "type": ["null", "string"] },
{ "name": "CreatedAt", "type": { "type": "long", "logicalType": "timestamp-millis" } }
]
}
""");
public object? Get(int fieldPos) => fieldPos switch
{
0 => Name,
1 => Email,
2 => CreatedAt,
_ => throw new global::Avro.AvroRuntimeException($"Invalid index {fieldPos}")
};
public void Put(int fieldPos, object? fieldValue)
{
switch (fieldPos)
{
case 0: Set_Name(this, (string)fieldValue!); break;
case 1: Set_Email(this, fieldValue as string); break;
case 2: Set_CreatedAt(this, (global::System.DateTime)fieldValue!); break;
default: throw new global::Avro.AvroRuntimeException($"Invalid index {fieldPos}");
}
}
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_Name")]
extern static void Set_Name(User obj, string value);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_Email")]
extern static void Set_Email(User obj, string? value);
[global::System.Runtime.CompilerServices.UnsafeAccessor(global::System.Runtime.CompilerServices.UnsafeAccessorKind.Method, Name = "set_CreatedAt")]
extern static void Set_CreatedAt(User obj, global::System.DateTime value);
}
#nullable restore
#pragma warning restore CS8618, CS8633, CS8714, CS8775, CS8981
The exact shape depends on configuration and the selected Avro library.
All generated types are declared as partial, allowing you to add members or behavior:
namespace example;
public partial record User
{
public override string ToString() => $"{Name} <{Email}>";
}
Configure the generator via MSBuild properties in your .csproj file.
Generated types are public by default. To make them internal:
<PropertyGroup>
<AvroSourceGeneratorAccessModifier>internal</AvroSourceGeneratorAccessModifier>
</PropertyGroup>
Supported values: public (default), internal.
By default, types are generated as record when possible. To use class instead:
<PropertyGroup>
<AvroSourceGeneratorRecordDeclaration>class</AvroSourceGeneratorRecordDeclaration>
</PropertyGroup>
[!NOTE]
Some schema kinds (e.g.,fixed,errorforApache.Avro) require classes due to inheritance constraints.
By default, the generator matches the consuming project’s C# version.
You can target an older version for broader compatibility:
<PropertyGroup>
<AvroSourceGeneratorLanguageFeatures>CSharp7_3</AvroSourceGeneratorLanguageFeatures>
</PropertyGroup>
This disables newer features such as records or nullable reference types.
Supported values are CSharp7_3, CSharp8, CSharp9, CSharp10, CSharp11, CSharp12, CSharp13.
The generator tries to detect the Avro library automatically from referenced packages. You can override this behavior by specifying the target library explicitly.
Code still generates, but without library-specific features. To suppress the warning and explicitly disable integration:
<PropertyGroup>
<AvroSourceGeneratorAvroLibrary>None</AvroSourceGeneratorAvroLibrary>
</PropertyGroup>
Specify one explicitly:
<PropertyGroup>
<AvroSourceGeneratorAvroLibrary>Apache</AvroSourceGeneratorAvroLibrary>
</PropertyGroup>
Supported values:
Auto (default) — detect automaticallyNone — no library-specific codeApache — target Apache.AvroChr — target Chr.AvroBy default, the generator fails on duplicate schema outputs.
A duplicate occurs when multiple .avsc files would generate the same fully qualified name.
This behavior can be configured using the following MSBuild property:
<PropertyGroup>
<AvroSourceGeneratorDuplicateResolution>Ignore</AvroSourceGeneratorDuplicateResolution>
</PropertyGroup>
Supported values:
Error (default) — emits a diagnostic and stops generationIgnore — Allows duplicates and selects one schema arbitrarily, ignoring the rest.[!Warning]
Ignore is nondeterministic and should only be used when duplicate outputs are intentional and safe.
Contributions are welcome!
If you encounter bugs, want to propose features, or improve docs, please open an issue or submit a pull request on GitHub.
Licensed under the MIT License.