private static async Task <IProducer <OrderKey, OrderEventRecord> > CreateProducer( ISchemaRegistryClient registryClient, AutomaticRegistrationBehavior automaticRegistrationBehavior) { var schemaBuilder = new SchemaBuilder( SchemaBuilder.CreateDefaultCaseBuilders() .Prepend(builder => new OrderEventUnionSchemaBuilderCase(builder))); using var serializerBuilder = new SchemaRegistrySerializerBuilder( registryClient, schemaBuilder, serializerBuilder: new BinarySerializerBuilder( BinarySerializerBuilder.CreateDefaultCaseBuilders() .Prepend(builder => new OrderEventUnionSerializerBuilderCase(builder)))); var producerBuilder = new ProducerBuilder <OrderKey, OrderEventRecord>( new ProducerConfig { BootstrapServers = BootstrapServers, }); await producerBuilder.SetAvroKeySerializer( registryClient, SubjectNameStrategy.Topic.ConstructKeySubjectName(Topic), automaticRegistrationBehavior); await producerBuilder.SetAvroValueSerializer( serializerBuilder, SubjectNameStrategy.Topic.ConstructKeySubjectName(Topic), automaticRegistrationBehavior); return(producerBuilder.Build()); }
/// <summary> /// Creates a serializer. /// </summary> /// <param name="registryClient"> /// The client to use for Schema Registry operations. (The client will not be disposed.) /// </param> /// <param name="registerAutomatically"> /// When to automatically register schemas that match the type being serialized. /// </param> /// <param name="schemaBuilder"> /// The schema builder to use to create a schema for a C# type when registering automatically. /// If none is provided, the default schema builder will be used. /// </param> /// <param name="schemaReader"> /// The JSON schema reader to use to convert schemas received from the registry into abstract /// representations. If none is provided, the default schema reader will be used. /// </param> /// <param name="schemaWriter"> /// The JSON schema writer to use to convert abstract schema representations when registering /// automatically. If none is provided, the default schema writer will be used. /// </param> /// <param name="serializerBuilder"> /// The deserializer builder to use to build serialization functions for C# types. If none /// is provided, the default serializer builder will be used. /// </param> /// <param name="subjectNameBuilder"> /// A function that determines the subject name given the topic name and a component type /// (key or value). If none is provided, the default "{topic name}-{component}" naming /// convention will be used. /// </param> /// <param name="tombstoneBehavior"> /// The behavior of the serializer on tombstone records. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when the registry client is null. /// </exception> public AsyncSchemaRegistrySerializer( ISchemaRegistryClient registryClient, AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never, Abstract.ISchemaBuilder schemaBuilder = null, IJsonSchemaReader schemaReader = null, IJsonSchemaWriter schemaWriter = null, IBinarySerializerBuilder serializerBuilder = null, Func <SerializationContext, string> subjectNameBuilder = null, TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None ) { if (registryClient == null) { throw new ArgumentNullException(nameof(registryClient)); } if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null) { throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values."); } RegisterAutomatically = registerAutomatically; SchemaBuilder = schemaBuilder ?? new Abstract.SchemaBuilder(); SchemaReader = schemaReader ?? new JsonSchemaReader(); SchemaWriter = schemaWriter ?? new JsonSchemaWriter(); SerializerBuilder = serializerBuilder ?? new BinarySerializerBuilder(); SubjectNameBuilder = subjectNameBuilder ?? (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}"); TombstoneBehavior = tombstoneBehavior; _cache = new Dictionary <string, Task <Func <T, byte[]> > >(); _register = (subject, json) => registryClient.RegisterSchemaAsync(subject, json); _resolve = subject => registryClient.GetLatestSchemaAsync(subject); }
/// <summary> /// Initializes a new instance of the <see cref="AsyncSchemaRegistrySerializer{T}" /> /// class with a Schema Registry configuration. /// </summary> /// <param name="registryConfiguration"> /// A Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is /// highly recommended. /// </param> /// <param name="registerAutomatically"> /// Whether the serializer should automatically register schemas that match the type being /// serialized. /// </param> /// <param name="schemaBuilder"> /// A schema builder instance that should be used to create schemas for .NET <see cref="Type" />s /// when registering automatically. If none is provided, the default <see cref="SchemaBuilder" /> /// will be used. /// </param> /// <param name="schemaReader"> /// A schema reader instance that should be used to convert schemas received from the /// Registry into abstract representations. If none is provided, the default /// <see cref="JsonSchemaReader" /> will be used. /// </param> /// <param name="schemaWriter"> /// A schema writer instance that should be used to convert abstract schema representations /// when registering automatically. If none is provided, the default <see cref="JsonSchemaWriter" /> /// will be used. /// </param> /// <param name="serializerBuilder"> /// A serializer builder instance that should be used to generate serialization functions /// for .NET <see cref="Type" />s. If none is provided, the default <see cref="BinarySerializerBuilder" /> /// will be used. /// </param> /// <param name="subjectNameBuilder"> /// A function that determines a subject name given the topic name and a component type /// (key or value). If none is provided, the default <c>{topic name}-{component}</c> naming /// convention will be used. /// </param> /// <param name="tombstoneBehavior"> /// How the serializer should handle tombstone records. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when <paramref name="registryConfiguration" /> is <c>null</c>. /// </exception> /// <exception cref="UnsupportedTypeException"> /// Thrown when <paramref name="tombstoneBehavior" /> is incompatible with /// <typeparamref name="T" />. /// </exception> public AsyncSchemaRegistrySerializer( IEnumerable <KeyValuePair <string, string> > registryConfiguration, AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never, Abstract.ISchemaBuilder schemaBuilder = null, IJsonSchemaReader schemaReader = null, IJsonSchemaWriter schemaWriter = null, IBinarySerializerBuilder serializerBuilder = null, Func <SerializationContext, string> subjectNameBuilder = null, TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None) { if (registryConfiguration == null) { throw new ArgumentNullException(nameof(registryConfiguration)); } if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null) { throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values."); } RegisterAutomatically = registerAutomatically; RegistryClient = new CachedSchemaRegistryClient(registryConfiguration); SchemaBuilder = schemaBuilder ?? new Abstract.SchemaBuilder(); SchemaReader = schemaReader ?? new JsonSchemaReader(); SchemaWriter = schemaWriter ?? new JsonSchemaWriter(); SerializerBuilder = serializerBuilder ?? new BinarySerializerBuilder(); SubjectNameBuilder = subjectNameBuilder ?? (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}"); TombstoneBehavior = tombstoneBehavior; cache = new Dictionary <string, Task <Func <T, byte[]> > >(); disposeRegistryClient = true; }
/// <summary> /// Creates a serializer. /// </summary> /// <param name="registryConfiguration"> /// Schema Registry configuration. Using the <see cref="SchemaRegistryConfig" /> class is /// highly recommended. /// </param> /// <param name="registerAutomatically"> /// When to automatically register schemas that match the type being serialized. /// </param> /// <param name="schemaBuilder"> /// The schema builder to use to create a schema for a C# type when registering automatically. /// If none is provided, the default schema builder will be used. /// </param> /// <param name="schemaReader"> /// The JSON schema reader to use to convert schemas received from the registry into abstract /// representations. If none is provided, the default schema reader will be used. /// </param> /// <param name="schemaWriter"> /// The JSON schema writer to use to convert abstract schema representations when registering /// automatically. If none is provided, the default schema writer will be used. /// </param> /// <param name="serializerBuilder"> /// The deserializer builder to use to build serialization functions for C# types. If none /// is provided, the default serializer builder will be used. /// </param> /// <param name="subjectNameBuilder"> /// A function that determines the subject name given the topic name and a component type /// (key or value). If none is provided, the default "{topic name}-{component}" naming /// convention will be used. /// </param> /// <param name="tombstoneBehavior"> /// The behavior of the serializer on tombstone records. /// </param> /// <exception cref="ArgumentNullException"> /// Thrown when the registry configuration is null. /// </exception> public AsyncSchemaRegistrySerializer( IEnumerable <KeyValuePair <string, string> > registryConfiguration, AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never, Abstract.ISchemaBuilder schemaBuilder = null, IJsonSchemaReader schemaReader = null, IJsonSchemaWriter schemaWriter = null, IBinarySerializerBuilder serializerBuilder = null, Func <SerializationContext, string> subjectNameBuilder = null, TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None ) { if (registryConfiguration == null) { throw new ArgumentNullException(nameof(registryConfiguration)); } if (tombstoneBehavior != TombstoneBehavior.None && default(T) != null) { throw new UnsupportedTypeException(typeof(T), $"{typeof(T)} cannot represent tombstone values."); } RegisterAutomatically = registerAutomatically; SchemaBuilder = schemaBuilder ?? new Abstract.SchemaBuilder(); SchemaReader = schemaReader ?? new JsonSchemaReader(); SchemaWriter = schemaWriter ?? new JsonSchemaWriter(); SerializerBuilder = serializerBuilder ?? new BinarySerializerBuilder(); SubjectNameBuilder = subjectNameBuilder ?? (c => $"{c.Topic}-{(c.Component == MessageComponentType.Key ? "key" : "value")}"); TombstoneBehavior = tombstoneBehavior; _cache = new Dictionary <string, Task <Func <T, byte[]> > >(); _register = async(subject, json) => { using (var registry = new CachedSchemaRegistryClient(registryConfiguration)) { return(await registry.RegisterSchemaAsync(subject, new Schema(json, SchemaType.Avro)).ConfigureAwait(false)); } }; _resolve = async subject => { using (var registry = new CachedSchemaRegistryClient(registryConfiguration)) { var schema = await registry.GetLatestSchemaAsync(subject).ConfigureAwait(false); if (schema.SchemaType != SchemaType.Avro) { throw new UnsupportedSchemaException(null, $"The latest schema with subject {subject} is not an Avro schema."); } return(schema); } }; }
/// <exception cref="UnsupportedTypeException"> /// Thrown when <typeparamref name="T" /> is incompatible with the retrieved schema. /// </exception> /// <inheritdoc /> public async Task <ISerializer <T> > Build <T>( string subject, AutomaticRegistrationBehavior registerAutomatically = AutomaticRegistrationBehavior.Never, TombstoneBehavior tombstoneBehavior = TombstoneBehavior.None) { switch (registerAutomatically) { case AutomaticRegistrationBehavior.Always: var json = SchemaWriter.Write(SchemaBuilder.BuildSchema <T>()); var id = await RegistryClient.RegisterSchemaAsync(subject, new Schema(json, SchemaType.Avro)).ConfigureAwait(false); return(Build <T>(id, json, tombstoneBehavior)); case AutomaticRegistrationBehavior.Never: var existing = await RegistryClient.GetLatestSchemaAsync(subject).ConfigureAwait(false); return(Build <T>(existing.Id, existing.SchemaString, tombstoneBehavior)); default: throw new ArgumentOutOfRangeException(nameof(registerAutomatically)); } }