使用 avroserializer 写 avro 文件前必须定义 schema,t 需标注 [datacontract]/[datamember],字段类型受限;须用 datafilewriter 写二进制文件,不可文本写入;读取时 schema 必须与写入一致,推荐 apache.avro 而非 confluent.schemaregistry 用于本地文件。

用 AvroSerializer<t></t> 写 AVRO 文件前,必须先定义 Schema
AVRO 不是“随便序列化对象就行”的格式,Schema 是强制前置条件。C# 里最常用的是 Confluent.SchemaRegistry.Serdes.AvroSerializer<t></t>(配合 Kafka)或纯 AvroConvert + BinaryEncoder(本地文件)。但无论哪种,T 的类型定义必须能映射出完整 Schema —— 比如字段不能是 object,集合得是 IList<t></t> 或 Array,且类需加 [DataContract] 和 [DataMember],否则运行时抛 AvroRuntimeException: Cannot resolve type。
- 用
[DataContract] public class Event { [DataMember(Order = 0)] public string Id { get; set; } },不是public record或无属性修饰的 class - Schema 可显式传入
new AvroSerializer<event>(schemaString)</event>,也可让库自动从类型推导(但要求类型干净、无循环引用) - 如果字段是
DateTime,默认映射为long(毫秒时间戳),要 UTC 语义就得自己处理时区,别指望自动转
File.Create() 后直接写二进制流,别用 StreamWriter
AVRO 文件是二进制容器格式(Object Container File),开头有 16 字节 magic bytes(Obj\x01),后面跟着 Schema、同步标记、数据块。用文本方式写会直接破坏结构,导致后续读取报 InvalidDataException: Not a valid Avro data file。
- 写文件必须用
using var fs = File.Create("data.avro"); using var writer = new BinaryWriter(fs); - 推荐用
Avro.File.DataFileWriter<t></t>(来自Apache.Avro包),它自动处理 magic、Schema 嵌入和块压缩:using var writer = new DataFileWriter<Event>(new FsSyncableOutputStream("data.avro")); writer.SetCodec(Codec.Null); writer.Append(new Event { Id = "abc" }); writer.Close(); - 别手动拼接 Schema JSON 字符串再写进文件——
DataFileWriter会把它序列化成二进制并写在文件头,你只管 Append 数据
读 DataFileReader<t></t> 时,Schema 必须和写入时一致
AVRO 读取不依赖运行时类型反射,而是靠文件内嵌 Schema 和你传入的 T 类型做字段名/顺序/类型比对。哪怕只是改了 [DataMember(Order = 0)] 里的数字,或给字段加了默认值,都可能触发 AvroRuntimeException: Field not found: xxx。
- 确保读取用的
T和写入用的是同一个编译版本的类(dll 不能混用) - 如果文件是用旧版 Schema 写的,而你现在用新类读,得用
DataFileReader<object></object>+ 手动解析,或者启用 Schema resolution(需要额外传入 writer schema 和 reader schema) - 读大文件时注意内存:默认
DataFileReader会把整个 block 解压进内存,若单条记录大、block 大,容易 OOM;可设reader.Configuration.MaxBlockSize = 64 * 1024
别忽略 Apache.Avro 和 Confluent.SchemaRegistry 的定位差异
前者是通用 AVRO 序列化库,适合离线文件读写;后者是 Kafka 生态专用,强依赖远程 Schema Registry 服务,本地读写 AVRO 文件时引入它反而多出 HTTP 依赖和配置负担。
- 纯文件场景,只装
Apache.Avro(注意 NuGet 包名是Apache.Avro,不是avro或Avro) -
Confluent.SchemaRegistry的AvroSerializer<t></t>默认不写 Schema 到文件,它只生成二进制消息体,Schema 存 Registry —— 直接拿它的输出去存成 .avro 文件,读的时候根本找不到 Schema - 如果项目已用 Confluent.Kafka,又想复用其序列化逻辑,得额外调用
schemaRegistryClient.GetLatestSchemaAsync(subject)拿到 Schema,再喂给DataFileWriter
实际用下来,Schema 定义和类型绑定那步最容易卡住——不是语法错,而是字段命名大小写、nullability、嵌套类的访问修饰符这些细节,都会让 Schema 推导失败。宁可多花两分钟手写 JSON Schema,也别赌自动推导能猜对你的心思。










