From 19ac3f8952b7e1f1751507fb2068f65dacc012d9 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:10:39 +0100 Subject: [PATCH 1/7] Make IEventSerializer AOT-compatible by removing suppression attributes Remove [RequiresUnreferencedCode] and [RequiresDynamicCode] from the IEventSerializer interface and all 43 consumer files across the codebase. Extract the reflection-based DefaultEventSerializer to a new Eventuous.Serialization.Json package. The core serialization interface and DefaultStaticEventSerializer are now fully AOT-clean. - Clean IEventSerializer interface (no AOT attributes) - Add EventSerializer static holder replacing DefaultEventSerializer.Instance - Move DefaultEventSerializer to Eventuous.Serialization.Json package - Remove AOT attributes from Persistence, Producers, Subscriptions, Application, and all integration packages (KurrentDB, Sql.Base, RabbitMQ, Sqlite) - Delete 6 Constants.cs files with DynamicSerializationMessage - Update all samples and tests to use new API Co-Authored-By: Claude Opus 4.6 --- Eventuous.slnx | 1 + samples/kurrentdb/Bookings/Bookings.csproj | 1 + samples/kurrentdb/Bookings/Program.cs | 2 +- samples/kurrentdb/Bookings/Registrations.cs | 2 +- samples/postgres/Bookings/Bookings.csproj | 1 + samples/postgres/Bookings/Registrations.cs | 2 +- .../Producers/ServiceBusProducer.cs | 2 +- .../ConvertEventToMessage.cs | 4 +-- .../AggregateService/CommandService.cs | 2 -- .../Diagnostics/TracedCommandService.cs | 2 -- .../FunctionalService/CommandService.cs | 2 -- .../Eventuous.Application/ICommandService.cs | 2 -- .../Persistence/WriterExtensions.cs | 4 --- .../ThrowingCommandService.cs | 2 -- .../AggregatePersistenceExtensions.cs | 10 ------- .../AggregateStore/AggregateStore.cs | 6 ---- .../AggregateStoreExtensions.cs | 4 --- .../AggregateStoreWithArchive.cs | 6 ---- .../AggregateStore/IAggregateStore.cs | 12 -------- .../src/Eventuous.Persistence/Constants.cs | 8 ----- .../Diagnostics/Tracing/TracedEventReader.cs | 4 --- .../Diagnostics/Tracing/TracedEventStore.cs | 8 ----- .../Diagnostics/Tracing/TracedEventWriter.cs | 4 --- .../EventStore/IEventReader.cs | 4 --- .../EventStore/IEventWriter.cs | 4 --- .../EventStore/StoreFunctions.cs | 10 ------- .../EventStore/TieredEventReader.cs | 4 --- .../EventStore/TieredEventStore.cs | 8 ----- .../StateStore/IStateStore.cs | 2 -- .../StateStore/StateStore.cs | 4 +-- .../StateStore/StateStoreFunctions.cs | 4 --- .../src/Eventuous.Producers/BaseProducer.cs | 10 ------- src/Core/src/Eventuous.Producers/Constants.cs | 8 ----- src/Core/src/Eventuous.Producers/IProducer.cs | 6 ---- .../Eventuous.Producers/ProducerExtensions.cs | 4 --- .../DefaultEventSerializer.cs | 27 +++++++++-------- .../Eventuous.Serialization.Json.csproj | 8 +++++ .../DefaultStaticEventSerializer.cs | 4 --- .../EventSerializer.cs | 29 +++++++++++++++++++ .../IEventSerializer.cs | 10 ------- .../src/Eventuous.Subscriptions/Constants.cs | 8 ----- .../EventSubscription.cs | 13 +-------- .../EventSubscriptionWithCheckpoint.cs | 7 ----- .../IMessageSubscription.cs | 2 -- .../Eventuous.Tests.Persistence.Base.csproj | 1 + .../Eventuous.Tests.Subscriptions.csproj | 1 + .../TestSetup.cs | 10 +++++++ .../ElasticPlayground.csproj | 1 + .../src/ElasticPlayground/Program.cs | 2 +- .../Eventuous.Sut.AspNetCore.csproj | 1 + .../test/Eventuous.Sut.AspNetCore/Program.cs | 2 +- .../Producers/GooglePubSubProducer.cs | 2 +- .../Producers/KafkaBasicProducer.cs | 2 +- .../BasicProducerTests.cs | 2 +- .../src/Eventuous.KurrentDB/Constants.cs | 8 ----- .../KurrentDBEventStore.cs | 18 +----------- .../Producers/KurrentDBProducer.cs | 6 +--- .../Subscriptions/AllStreamSubscription.cs | 4 --- .../PersistentSubscriptionBase.cs | 4 --- .../Subscriptions/StreamSubscription.cs | 4 --- .../Subscriptions/CustomDependenciesTests.cs | 2 +- ...Eventuous.Tests.Projections.MongoDB.csproj | 1 + .../Fixtures/IntegrationFixture.cs | 2 +- .../src/Eventuous.RabbitMq/Constants.cs | 8 ----- .../Producers/RabbitMqProducer.cs | 6 +--- .../Subscriptions/RabbitMqSubscription.cs | 6 ---- src/Redis/src/Eventuous.Redis/RedisStore.cs | 2 +- .../Eventuous.Tests.Redis.csproj | 1 + .../Fixtures/IntegrationFixture.cs | 2 +- .../src/Eventuous.Sql.Base/Constants.cs | 8 ----- .../Producers/UniversalProducer.cs | 2 -- .../Eventuous.Sql.Base/SqlEventStoreBase.cs | 22 +------------- .../Subscriptions/SqlSubscriptionBase.cs | 10 ------- .../src/Eventuous.Sqlite/SqliteStore.cs | 6 ---- .../Eventuous.Tests.Sqlite.csproj | 1 + 75 files changed, 93 insertions(+), 321 deletions(-) delete mode 100644 src/Core/src/Eventuous.Persistence/Constants.cs delete mode 100644 src/Core/src/Eventuous.Producers/Constants.cs rename src/Core/src/{Eventuous.Serialization => Eventuous.Serialization.Json}/DefaultEventSerializer.cs (52%) create mode 100644 src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj create mode 100644 src/Core/src/Eventuous.Serialization/EventSerializer.cs delete mode 100644 src/Core/src/Eventuous.Subscriptions/Constants.cs create mode 100644 src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs delete mode 100644 src/KurrentDB/src/Eventuous.KurrentDB/Constants.cs delete mode 100644 src/RabbitMq/src/Eventuous.RabbitMq/Constants.cs delete mode 100644 src/Relational/src/Eventuous.Sql.Base/Constants.cs diff --git a/Eventuous.slnx b/Eventuous.slnx index 83e61f82a..c564925de 100644 --- a/Eventuous.slnx +++ b/Eventuous.slnx @@ -52,6 +52,7 @@ + diff --git a/samples/kurrentdb/Bookings/Bookings.csproj b/samples/kurrentdb/Bookings/Bookings.csproj index 361244a4b..13246f694 100644 --- a/samples/kurrentdb/Bookings/Bookings.csproj +++ b/samples/kurrentdb/Bookings/Bookings.csproj @@ -35,6 +35,7 @@ + diff --git a/samples/kurrentdb/Bookings/Program.cs b/samples/kurrentdb/Bookings/Program.cs index ec51b0fd3..0060b1549 100644 --- a/samples/kurrentdb/Bookings/Program.cs +++ b/samples/kurrentdb/Bookings/Program.cs @@ -22,7 +22,7 @@ // .WriteTo.Seq("http://localhost:5341") .CreateLogger(); -DefaultEventSerializer.SetDefaultSerializer(new DefaultStaticEventSerializer(new SourceGenerationContext())); +EventSerializer.SetDefault(new DefaultStaticEventSerializer(new SourceGenerationContext())); var builder = WebApplication.CreateBuilder(args); builder.Host.UseSerilog(); diff --git a/samples/kurrentdb/Bookings/Registrations.cs b/samples/kurrentdb/Bookings/Registrations.cs index 5bc7d77c8..b778fdd9b 100644 --- a/samples/kurrentdb/Bookings/Registrations.cs +++ b/samples/kurrentdb/Bookings/Registrations.cs @@ -23,7 +23,7 @@ namespace Bookings; public static class Registrations { extension(IServiceCollection services) { public void AddEventuous(IConfiguration configuration) { - DefaultEventSerializer.SetDefaultSerializer( + EventSerializer.SetDefault( new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web).ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)) ); diff --git a/samples/postgres/Bookings/Bookings.csproj b/samples/postgres/Bookings/Bookings.csproj index 258c4d860..6e82ed4fa 100644 --- a/samples/postgres/Bookings/Bookings.csproj +++ b/samples/postgres/Bookings/Bookings.csproj @@ -31,5 +31,6 @@ + \ No newline at end of file diff --git a/samples/postgres/Bookings/Registrations.cs b/samples/postgres/Bookings/Registrations.cs index 78a7040a9..ce1f6d3dd 100644 --- a/samples/postgres/Bookings/Registrations.cs +++ b/samples/postgres/Bookings/Registrations.cs @@ -19,7 +19,7 @@ namespace Bookings; public static class Registrations { public static void AddEventuous(this IServiceCollection services, IConfiguration configuration) { - DefaultEventSerializer.SetDefaultSerializer( + EventSerializer.SetDefault( new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web).ConfigureForNodaTime(DateTimeZoneProviders.Tzdb)) ); diff --git a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProducer.cs b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProducer.cs index bcd834291..e48000877 100644 --- a/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProducer.cs +++ b/src/Azure/src/Eventuous.Azure.ServiceBus/Producers/ServiceBusProducer.cs @@ -40,7 +40,7 @@ public ServiceBusProducer( _options = options; _log = log; _sender = client.CreateSender(options.QueueOrTopicName, options.SenderOptions); - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _messageBatchBuilder = new(_sender, this._serializer, options.AttributeNames, SetActivityMessageType); log?.LogInformation("ServiceBusProducer created for {QueueOrTopicName}", options.QueueOrTopicName); } diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs index 9abd8e54a..a024e1da8 100644 --- a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs +++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/ConvertEventToMessage.cs @@ -9,7 +9,7 @@ public class ConvertEventToMessage { public ConvertEventToMessage() { var builder = new ServiceBusMessageBuilder( - DefaultEventSerializer.Instance, + EventSerializer.Default, "test-stream", new(), new() { @@ -92,7 +92,7 @@ public class WithMessagePropertiesInMetaData { public WithMessagePropertiesInMetaData() { var attributeNames = new ServiceBusMessageAttributeNames(); - var builder = new ServiceBusMessageBuilder(DefaultEventSerializer.Instance, "test-stream", attributeNames, new()); + var builder = new ServiceBusMessageBuilder(EventSerializer.Default, "test-stream", attributeNames, new()); _message = builder.CreateServiceBusMessage( new( diff --git a/src/Core/src/Eventuous.Application/AggregateService/CommandService.cs b/src/Core/src/Eventuous.Application/AggregateService/CommandService.cs index 45111b4de..a48c79e1d 100644 --- a/src/Core/src/Eventuous.Application/AggregateService/CommandService.cs +++ b/src/Core/src/Eventuous.Application/AggregateService/CommandService.cs @@ -59,8 +59,6 @@ protected IDefineExpectedState On() /// Cancellation token /// of the execution /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task> Handle(TCommand command, CancellationToken cancellationToken) where TCommand : class { if (!_handlers.TryGet(out var registeredHandler)) { Log.CommandHandlerNotFound(); diff --git a/src/Core/src/Eventuous.Application/Diagnostics/TracedCommandService.cs b/src/Core/src/Eventuous.Application/Diagnostics/TracedCommandService.cs index be3c6c81d..8de58ca9b 100644 --- a/src/Core/src/Eventuous.Application/Diagnostics/TracedCommandService.cs +++ b/src/Core/src/Eventuous.Application/Diagnostics/TracedCommandService.cs @@ -18,8 +18,6 @@ namespace Eventuous.Diagnostics; InnerService = appService; } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task> Handle(TCommand command, CancellationToken cancellationToken) where TCommand : class => CommandServiceActivity.TryExecute( diff --git a/src/Core/src/Eventuous.Application/FunctionalService/CommandService.cs b/src/Core/src/Eventuous.Application/FunctionalService/CommandService.cs index 5563b2f2e..83f10e043 100644 --- a/src/Core/src/Eventuous.Application/FunctionalService/CommandService.cs +++ b/src/Core/src/Eventuous.Application/FunctionalService/CommandService.cs @@ -66,8 +66,6 @@ protected CommandService(IEventStore store, ITypeMapper? typeMap = null, AmendEv /// Command type /// instance /// Throws when there's no command handler was registered for the command type - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task> Handle(TCommand command, CancellationToken cancellationToken) where TCommand : class { if (!_handlers.TryGet(out var registeredHandler)) { Log.CommandHandlerNotFound(); diff --git a/src/Core/src/Eventuous.Application/ICommandService.cs b/src/Core/src/Eventuous.Application/ICommandService.cs index 132aa39df..b6b4fe241 100644 --- a/src/Core/src/Eventuous.Application/ICommandService.cs +++ b/src/Core/src/Eventuous.Application/ICommandService.cs @@ -6,8 +6,6 @@ namespace Eventuous; public interface ICommandService where TState : State, new() { - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task> Handle(TCommand command, CancellationToken cancellationToken) where TCommand : class; } diff --git a/src/Core/src/Eventuous.Application/Persistence/WriterExtensions.cs b/src/Core/src/Eventuous.Application/Persistence/WriterExtensions.cs index 88213674c..b7a80e87f 100644 --- a/src/Core/src/Eventuous.Application/Persistence/WriterExtensions.cs +++ b/src/Core/src/Eventuous.Application/Persistence/WriterExtensions.cs @@ -5,8 +5,6 @@ namespace Eventuous.Persistence; static class WriterExtensions { extension(IEventWriter writer) { - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Store(ProposedAppend append, AmendEvent? amendEvent, CancellationToken cancellationToken) { Ensure.NotNull(append.Events); @@ -33,8 +31,6 @@ NewStreamEvent ToStreamEvent(ProposedEvent evt) { } } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Store( IReadOnlyCollection appends, AmendEvent? amendEvent, diff --git a/src/Core/src/Eventuous.Application/ThrowingCommandService.cs b/src/Core/src/Eventuous.Application/ThrowingCommandService.cs index 296e9e629..cc49091de 100644 --- a/src/Core/src/Eventuous.Application/ThrowingCommandService.cs +++ b/src/Core/src/Eventuous.Application/ThrowingCommandService.cs @@ -10,8 +10,6 @@ namespace Eventuous; /// public class ThrowingCommandService(ICommandService inner) : ICommandService where TState : State, new() { - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task> Handle(TCommand command, CancellationToken cancellationToken) where TCommand : class { var result = await inner.Handle(command, cancellationToken); diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs index 1907e364a..8f9c7cc14 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregatePersistenceExtensions.cs @@ -20,8 +20,6 @@ public static class AggregatePersistenceExtensions { /// Aggregate state type /// Append event result /// Gets thrown if the expected stream version mismatches with the given original stream version - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task StoreAggregate( StreamName streamName, TAggregate aggregate, @@ -52,8 +50,6 @@ public async Task StoreAggregate( /// Aggregate identity type /// Append event result /// Gets thrown if the expected stream version mismatches with the given original stream version - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task StoreAggregate( TAggregate aggregate, TId id, @@ -84,8 +80,6 @@ public Task StoreAggregate( /// Aggregate identity type /// Append event result /// Gets thrown if the expected stream version mismatches with the given original stream version - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task StoreAggregate( TAggregate aggregate, StreamNameMap? streamNameMap = null, @@ -115,8 +109,6 @@ public Task StoreAggregate( /// Aggregate instance /// If failIfNotFound set to true, this exception is thrown if there's no stream /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>( StreamName streamName, bool failIfNotFound = true, @@ -155,8 +147,6 @@ public Task StoreAggregate( /// Aggregate instance /// If failIfNotFound set to true, this exception is thrown if there's no stream /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task LoadAggregate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>( TId aggregateId, StreamNameMap? streamNameMap = null, diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStore.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStore.cs index aeda1b036..3884b355b 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStore.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStore.cs @@ -32,23 +32,17 @@ public AggregateStore( /// [Obsolete("Use IEventWriter.StoreAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Store(StreamName streamName, TAggregate aggregate, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new() => _eventWriter.StoreAggregate(streamName, aggregate, _amendEvent, cancellationToken); /// [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Load<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, TState>(StreamName streamName, CancellationToken cancellationToken) where T : Aggregate where TState : State, new() => _eventReader.LoadAggregate(streamName, true, _factoryRegistry, cancellationToken); /// [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, TState>(StreamName streamName, CancellationToken cancellationToken) where T : Aggregate where TState : State, new() => _eventReader.LoadAggregate(streamName, false, _factoryRegistry, cancellationToken); diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs index 296b78582..3f8fbc152 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreExtensions.cs @@ -17,8 +17,6 @@ public static class AggregateStoreExtensions { /// Aggregate id type /// [Obsolete("Use IEventReader.LoadAggregates instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Load <[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T, TState, TId>(StreamNameMap streamNameMap, TId id, CancellationToken cancellationToken) where T : Aggregate where TId : Id where TState : State, new() { @@ -39,8 +37,6 @@ public async Task Load /// Aggregate id type /// [Obsolete("Use IEventReader.LoadAggregates instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>( StreamNameMap streamNameMap, TId id, diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreWithArchive.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreWithArchive.cs index 52ae93beb..1e80ab7a6 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreWithArchive.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateStore/AggregateStoreWithArchive.cs @@ -15,22 +15,16 @@ public class AggregateStore( readonly TieredEventStore _tieredEventStore = new(eventStore, archiveReader); /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Store(StreamName streamName, TAggregate aggregate, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new() => eventStore.StoreAggregate(streamName, aggregate, amendEvent, cancellationToken); /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Load<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(StreamName streamName, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new() => _tieredEventStore.LoadAggregate(streamName, true, _factoryRegistry, cancellationToken); /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(StreamName streamName, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new() => _tieredEventStore.LoadAggregate(streamName, false, _factoryRegistry, cancellationToken); diff --git a/src/Core/src/Eventuous.Persistence/AggregateStore/IAggregateStore.cs b/src/Core/src/Eventuous.Persistence/AggregateStore/IAggregateStore.cs index a1eeeff67..df8631072 100644 --- a/src/Core/src/Eventuous.Persistence/AggregateStore/IAggregateStore.cs +++ b/src/Core/src/Eventuous.Persistence/AggregateStore/IAggregateStore.cs @@ -20,8 +20,6 @@ public interface IAggregateStore { /// Aggregate state type /// Result of the append operation [Obsolete("Use IEventWriter.StoreAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Store(TAggregate aggregate, TId id, CancellationToken cancellationToken) where TAggregate : Aggregate where TId : Id where TState : State, new() => Store(StreamNameFactory.For(id), aggregate, cancellationToken); @@ -36,8 +34,6 @@ public Task Store(TAggregate aggreg /// Aggregate state type /// Result of the append operation [Obsolete("Use IEventWriter.StoreAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task Store(StreamName streamName, TAggregate aggregate, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new(); @@ -51,8 +47,6 @@ Task Store(StreamName streamName, TAggre /// Aggregate identity type /// Aggregate instance [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Load<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(TId id, CancellationToken cancellationToken) where TAggregate : Aggregate where TId : Id where TState : State, new() => Load(StreamNameFactory.For(id), cancellationToken); @@ -66,8 +60,6 @@ Task Store(StreamName streamName, TAggre /// Aggregate state type /// Aggregate instance [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task Load<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(StreamName streamName, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new(); @@ -82,8 +74,6 @@ Task Store(StreamName streamName, TAggre /// Aggregate identity type /// Aggregate instance [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState, TId>(TId id, CancellationToken cancellationToken) where TAggregate : Aggregate where TId : Id where TState : State, new() => LoadOrNew(StreamNameFactory.For(id), cancellationToken); @@ -98,8 +88,6 @@ Task Store(StreamName streamName, TAggre /// Aggregate state type /// Aggregate instance [Obsolete("Use IEventReader.LoadAggregate instead.")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task LoadOrNew<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TAggregate, TState>(StreamName streamName, CancellationToken cancellationToken) where TAggregate : Aggregate where TState : State, new(); } diff --git a/src/Core/src/Eventuous.Persistence/Constants.cs b/src/Core/src/Eventuous.Persistence/Constants.cs deleted file mode 100644 index 3f1af6160..000000000 --- a/src/Core/src/Eventuous.Persistence/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous; - -internal static class AttrConstants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventReader.cs b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventReader.cs index 1f37de407..f9b8048ff 100644 --- a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventReader.cs +++ b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventReader.cs @@ -14,13 +14,9 @@ public class TracedEventReader(IEventReader reader) : BaseTracer, IEventReader { IEventReader Inner { get; } = reader; - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => TraceEnumerable(stream, Operations.ReadEvents, Inner.ReadEvents(stream, start, count, cancellationToken)); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => TraceEnumerable(stream, Operations.ReadEvents, Inner.ReadEventsBackwards(stream, start, count, cancellationToken)); diff --git a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventStore.cs b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventStore.cs index 0ed2fe8ea..4b0ab156c 100644 --- a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventStore.cs +++ b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventStore.cs @@ -20,8 +20,6 @@ public class TracedEventStore(IEventStore eventStore) : BaseTracer, IEventStore public Task StreamExists(StreamName stream, CancellationToken cancellationToken) => Trace(stream, Operations.StreamExists, () => Inner.StreamExists(stream, cancellationToken)); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -30,18 +28,12 @@ CancellationToken cancellationToken ) => Writer.AppendEvents(stream, expectedVersion, events, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) => Writer.AppendEvents(appends, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => Reader.ReadEvents(stream, start, count, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => Reader.ReadEventsBackwards(stream, start, count, cancellationToken); diff --git a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventWriter.cs b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventWriter.cs index 1df507c52..1c95d69a6 100644 --- a/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventWriter.cs +++ b/src/Core/src/Eventuous.Persistence/Diagnostics/Tracing/TracedEventWriter.cs @@ -13,8 +13,6 @@ public class TracedEventWriter(IEventWriter writer) : BaseTracer, IEventWriter { readonly string _componentName = writer.GetType().Name; - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -42,8 +40,6 @@ CancellationToken cancellationToken } } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) { if (appends.Count == 0) return []; diff --git a/src/Core/src/Eventuous.Persistence/EventStore/IEventReader.cs b/src/Core/src/Eventuous.Persistence/EventStore/IEventReader.cs index c419dc194..8268287b9 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/IEventReader.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/IEventReader.cs @@ -13,8 +13,6 @@ public interface IEventReader { /// How many events to read /// Cancellation token /// An async enumerable of events retrieved from the stream - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken); /// @@ -26,7 +24,5 @@ public interface IEventReader { /// How many events to read /// Cancellation token /// An async enumerable of events retrieved from the stream - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken); } diff --git a/src/Core/src/Eventuous.Persistence/EventStore/IEventWriter.cs b/src/Core/src/Eventuous.Persistence/EventStore/IEventWriter.cs index cef4b6d2b..490f3568a 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/IEventWriter.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/IEventWriter.cs @@ -13,8 +13,6 @@ public interface IEventWriter { /// Cancellation token /// Append result, which contains the global position of the last written event, /// as well as the next stream version - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -30,8 +28,6 @@ CancellationToken cancellationToken /// Collection of stream appends to perform /// Cancellation token /// Array of append results, one per stream in the same order as input - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] async Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) { var results = new AppendEventsResult[appends.Count]; var i = 0; diff --git a/src/Core/src/Eventuous.Persistence/EventStore/StoreFunctions.cs b/src/Core/src/Eventuous.Persistence/EventStore/StoreFunctions.cs index 4f59d9344..35d8e9689 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/StoreFunctions.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/StoreFunctions.cs @@ -17,8 +17,6 @@ public static class StoreFunctions { /// Append events result /// Any exception that occurred in the event store /// Gets thrown if the expected stream version mismatches with the given original stream version - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Store( StreamName streamName, ExpectedStreamVersion expectedStreamVersion, @@ -53,8 +51,6 @@ NewStreamEvent ToStreamEvent(object evt) { } } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Store( IReadOnlyCollection<(StreamName StreamName, ExpectedStreamVersion ExpectedVersion, IReadOnlyCollection Changes)> streams, AmendEvent? amendEvent = null, @@ -104,8 +100,6 @@ static NewStreamEvent ToStreamEvent(object evt, AmendEvent? amendEvent) { /// Throw an exception if the stream is not found /// Cancellation token /// An array with events retrieved from the stream - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public static async Task ReadEvents( this IEventReader eventReader, StreamName stream, @@ -138,8 +132,6 @@ CancellationToken cancellationToken /// Throw an exception if the stream is not found /// Cancellation token /// An array with events retrieved from the stream - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public static async Task ReadEventsBackwards( this IEventReader eventReader, StreamName stream, @@ -171,8 +163,6 @@ CancellationToken cancellationToken /// stream with the given name found in the store, the function will return an empty collection. /// Cancellation token /// Collection of events wrapped in - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public static async Task ReadStream( this IEventReader eventReader, StreamName streamName, diff --git a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs index ded73a241..7c00b2cef 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventReader.cs @@ -12,8 +12,6 @@ namespace Eventuous; /// Event reader pointing to hot store /// Event reader pointing to archive store public class TieredEventReader(IEventReader hotReader, IEventReader archiveReader) : IEventReader { - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEvents(StreamName streamName, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken) { var hotEvents = await LoadStreamEvents(hotReader, streamName, start, count, cancellationToken).NoContext(); @@ -42,8 +40,6 @@ public async IAsyncEnumerable ReadEvents(StreamName streamName, Str if (!any) throw new StreamNotFound(streamName); } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEventsBackwards(StreamName streamName, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken) { var hotEvents = await LoadStreamEvents(hotReader, streamName, start, count, cancellationToken, backwards: true).NoContext(); diff --git a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventStore.cs b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventStore.cs index a282a18d3..5d184f4a5 100644 --- a/src/Core/src/Eventuous.Persistence/EventStore/TieredEventStore.cs +++ b/src/Core/src/Eventuous.Persistence/EventStore/TieredEventStore.cs @@ -14,18 +14,12 @@ namespace Eventuous; public class TieredEventStore(IEventStore hotStore, IEventReader archiveReader) : IEventStore { readonly TieredEventReader _tieredReader = new(Ensure.NotNull(hotStore), Ensure.NotNull(archiveReader)); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => _tieredReader.ReadEvents(stream, start, count, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) => _tieredReader.ReadEventsBackwards(stream, start, count, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -33,8 +27,6 @@ public Task AppendEvents( CancellationToken cancellationToken ) => hotStore.AppendEvents(stream, expectedVersion, events, cancellationToken); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) => hotStore.AppendEvents(appends, cancellationToken); diff --git a/src/Core/src/Eventuous.Persistence/StateStore/IStateStore.cs b/src/Core/src/Eventuous.Persistence/StateStore/IStateStore.cs index 0a5418d0c..a8abcc707 100644 --- a/src/Core/src/Eventuous.Persistence/StateStore/IStateStore.cs +++ b/src/Core/src/Eventuous.Persistence/StateStore/IStateStore.cs @@ -18,7 +18,5 @@ public interface IStateStore { /// /// [Obsolete("Use IEventReader.LoadState instead")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task LoadState(StreamName stream, CancellationToken cancellationToken) where T : State, new(); } \ No newline at end of file diff --git a/src/Core/src/Eventuous.Persistence/StateStore/StateStore.cs b/src/Core/src/Eventuous.Persistence/StateStore/StateStore.cs index 8e1d29aac..5acdb3a37 100644 --- a/src/Core/src/Eventuous.Persistence/StateStore/StateStore.cs +++ b/src/Core/src/Eventuous.Persistence/StateStore/StateStore.cs @@ -9,13 +9,11 @@ namespace Eventuous; [Obsolete("Use IEventReader extension functions to load state")] public class StateStore(IEventReader eventReader, IEventSerializer? serializer = null) : IStateStore { readonly IEventReader _eventReader = Ensure.NotNull(eventReader); - readonly IEventSerializer _serializer = serializer ?? DefaultEventSerializer.Instance; + readonly IEventSerializer _serializer = serializer ?? EventSerializer.Default; const int PageSize = 500; [Obsolete("Use IEventReader.LoadState instead")] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task LoadState(StreamName stream, CancellationToken cancellationToken) where T : State, new() { var state = new T(); diff --git a/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs b/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs index 75d4667f0..9d32aaadb 100644 --- a/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs +++ b/src/Core/src/Eventuous.Persistence/StateStore/StateStoreFunctions.cs @@ -17,8 +17,6 @@ public static class StateStoreFunctions { /// State object type /// Instance of containing events and folded state /// Thrown if there's no stream and failIfNotFound is true - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task> LoadState( StreamName streamName, bool failIfNotFound = true, @@ -49,8 +47,6 @@ public async Task> LoadState( /// State object type /// State identity type /// Instance of containing events and folded state - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task> LoadState( StreamNameMap streamNameMap, TId id, diff --git a/src/Core/src/Eventuous.Producers/BaseProducer.cs b/src/Core/src/Eventuous.Producers/BaseProducer.cs index 81d0b880f..ca37df452 100644 --- a/src/Core/src/Eventuous.Producers/BaseProducer.cs +++ b/src/Core/src/Eventuous.Producers/BaseProducer.cs @@ -18,20 +18,14 @@ protected BaseProducer(ProducerTracingOptions? tracingOptions = null) { // ReSharper disable once MemberCanBePrivate.Global protected KeyValuePair[] DefaultTags { get; } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] protected abstract Task ProduceMessages(StreamName stream, IEnumerable messages, TProduceOptions? options, CancellationToken cancellationToken = default); protected void SetActivityMessageType(string messageType) => Activity.Current?.SetTag(Message.Type, messageType); - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Produce(StreamName stream, IEnumerable messages, CancellationToken cancellationToken = default) => Produce(stream, messages, null, cancellationToken); /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async Task Produce(StreamName stream, IEnumerable messages, TProduceOptions? options, CancellationToken cancellationToken = default) { var messagesArray = messages.ToArray(); if (messagesArray.Length == 0) return; @@ -56,8 +50,6 @@ public async Task Produce(StreamName stream, IEnumerable messag } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Produce(IReadOnlyCollection> requests, CancellationToken cancellationToken = default) { if (requests.Count == 0) return Task.CompletedTask; @@ -65,8 +57,6 @@ public Task Produce(IReadOnlyCollection> request } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task Produce(IReadOnlyCollection requests, CancellationToken cancellationToken = default) { if (requests.Count == 0) return Task.CompletedTask; diff --git a/src/Core/src/Eventuous.Producers/Constants.cs b/src/Core/src/Eventuous.Producers/Constants.cs deleted file mode 100644 index 675660347..000000000 --- a/src/Core/src/Eventuous.Producers/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.Producers; - -internal static class AttrConstants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/Core/src/Eventuous.Producers/IProducer.cs b/src/Core/src/Eventuous.Producers/IProducer.cs index 8e44d8511..57b601e88 100644 --- a/src/Core/src/Eventuous.Producers/IProducer.cs +++ b/src/Core/src/Eventuous.Producers/IProducer.cs @@ -13,8 +13,6 @@ public interface IProducer { /// Collection of messages to produce /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task Produce(StreamName stream, IEnumerable messages, CancellationToken cancellationToken = default); /// @@ -23,8 +21,6 @@ public interface IProducer { /// Collection of produce requests, one per target stream /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task Produce(IReadOnlyCollection requests, CancellationToken cancellationToken = default) => Task.WhenAll(requests.Select(r => Produce(r.Stream, r.Messages, cancellationToken))); } @@ -39,8 +35,6 @@ public interface IProducer : IProducer where TProduceOptions /// Produce options /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] Task Produce(StreamName stream, IEnumerable messages, TProduceOptions? options, CancellationToken cancellationToken = default); } diff --git a/src/Core/src/Eventuous.Producers/ProducerExtensions.cs b/src/Core/src/Eventuous.Producers/ProducerExtensions.cs index 9e042dd65..29eeb42e4 100644 --- a/src/Core/src/Eventuous.Producers/ProducerExtensions.cs +++ b/src/Core/src/Eventuous.Producers/ProducerExtensions.cs @@ -19,8 +19,6 @@ public static class ProducerExtensions { /// /// Message typ /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public static Task Produce( this IProducer producer, StreamName stream, @@ -55,8 +53,6 @@ public static Task Produce( /// Message type /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public static Task Produce( this IProducer producer, StreamName stream, diff --git a/src/Core/src/Eventuous.Serialization/DefaultEventSerializer.cs b/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs similarity index 52% rename from src/Core/src/Eventuous.Serialization/DefaultEventSerializer.cs rename to src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs index 5922d44db..03c9a5515 100644 --- a/src/Core/src/Eventuous.Serialization/DefaultEventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs @@ -7,34 +7,37 @@ namespace Eventuous; [PublicAPI] -public class DefaultEventSerializer(JsonSerializerOptions options, ITypeMapper? typeMapper = null) : IEventSerializer { - static readonly JsonSerializerOptions Options = new(JsonSerializerDefaults.Web); +public class DefaultEventSerializer : IEventSerializer { + readonly JsonSerializerOptions _options; + readonly ITypeMapper _typeMapper; - public static IEventSerializer Instance { get; private set; } = new DefaultEventSerializer(Options); + public DefaultEventSerializer(JsonSerializerOptions options, ITypeMapper? typeMapper = null) { + _options = options; + _typeMapper = typeMapper ?? TypeMap.Instance; - readonly ITypeMapper _typeMapper = typeMapper ?? TypeMap.Instance; - - public static void SetDefaultSerializer(IEventSerializer serializer) => Instance = serializer; + // Auto-register as default if none is set + EventSerializer.SetDefault(this); + } - [RequiresUnreferencedCode(IEventSerializer.SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(IEventSerializer.SerializationRequiresDynamicCodeMessage)] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This class is not intended for AOT use.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This class is not intended for AOT use.")] public DeserializationResult DeserializeEvent(ReadOnlySpan data, string eventType, string contentType) { var typeMapped = _typeMapper.TryGetType(eventType, out var dataType); if (!typeMapped) return new FailedToDeserialize(DeserializationError.UnknownType); if (contentType != ContentType) return new FailedToDeserialize(DeserializationError.ContentTypeMismatch); - var deserialized = JsonSerializer.Deserialize(data, dataType!, options); + var deserialized = JsonSerializer.Deserialize(data, dataType!, _options); return deserialized != null ? new SuccessfullyDeserialized(deserialized) : new FailedToDeserialize(DeserializationError.PayloadEmpty); } - [RequiresUnreferencedCode(IEventSerializer.SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(IEventSerializer.SerializationRequiresDynamicCodeMessage)] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This class is not intended for AOT use.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This class is not intended for AOT use.")] public SerializationResult SerializeEvent(object evt) - => new(_typeMapper.GetTypeName(evt), ContentType, JsonSerializer.SerializeToUtf8Bytes(evt, options)); + => new(_typeMapper.GetTypeName(evt), ContentType, JsonSerializer.SerializeToUtf8Bytes(evt, _options)); public string ContentType { get; } = "application/json"; } diff --git a/src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj b/src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj new file mode 100644 index 000000000..5320b5b8e --- /dev/null +++ b/src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj @@ -0,0 +1,8 @@ + + + Eventuous + + + + + diff --git a/src/Core/src/Eventuous.Serialization/DefaultStaticEventSerializer.cs b/src/Core/src/Eventuous.Serialization/DefaultStaticEventSerializer.cs index 18b38a468..31dc927a3 100644 --- a/src/Core/src/Eventuous.Serialization/DefaultStaticEventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization/DefaultStaticEventSerializer.cs @@ -11,8 +11,6 @@ namespace Eventuous; public class DefaultStaticEventSerializer(JsonSerializerContext context, ITypeMapper? typeMapper = null) : IEventSerializer { readonly ITypeMapper _typeMapper = typeMapper ?? TypeMap.Instance; - [UnconditionalSuppressMessage("Trimming", "IL2046", Justification = "This implementation is not using reflection.")] - [UnconditionalSuppressMessage("Trimming", "IL3051", Justification = "This implementation is not using reflection.")] public DeserializationResult DeserializeEvent(ReadOnlySpan data, string eventType, string contentType) { var typeMapped = _typeMapper.TryGetType(eventType, out var dataType); @@ -26,8 +24,6 @@ public DeserializationResult DeserializeEvent(ReadOnlySpan data, string ev : new FailedToDeserialize(DeserializationError.PayloadEmpty); } - [UnconditionalSuppressMessage("Trimming", "IL2046", Justification = "This implementation is not using reflection.")] - [UnconditionalSuppressMessage("Trimming", "IL3051", Justification = "This implementation is not using reflection.")] public SerializationResult SerializeEvent(object evt) => new(_typeMapper.GetTypeName(evt), ContentType, JsonSerializer.SerializeToUtf8Bytes(evt, evt.GetType(), context)); diff --git a/src/Core/src/Eventuous.Serialization/EventSerializer.cs b/src/Core/src/Eventuous.Serialization/EventSerializer.cs new file mode 100644 index 000000000..c5f8b6f7d --- /dev/null +++ b/src/Core/src/Eventuous.Serialization/EventSerializer.cs @@ -0,0 +1,29 @@ +// Copyright (C) Eventuous HQ OÜ. All rights reserved +// Licensed under the Apache License, Version 2.0. + +namespace Eventuous; + +/// +/// Static holder for the default event serializer instance. +/// Must be configured before use — either via or DI registration. +/// For AOT applications, use with a . +/// +[PublicAPI] +public static class EventSerializer { + static IEventSerializer? _default; + + /// + /// Gets the default event serializer. Throws if not configured. + /// + public static IEventSerializer Default => _default + ?? throw new InvalidOperationException( + "No default event serializer configured. " + + "Call EventSerializer.SetDefault() at startup or register IEventSerializer in DI. " + + "For AOT, use DefaultStaticEventSerializer with a JsonSerializerContext."); + + /// + /// Sets the default event serializer instance. + /// + public static void SetDefault(IEventSerializer serializer) + => _default = serializer ?? throw new ArgumentNullException(nameof(serializer)); +} diff --git a/src/Core/src/Eventuous.Serialization/IEventSerializer.cs b/src/Core/src/Eventuous.Serialization/IEventSerializer.cs index 1b52eebf8..d8bc08865 100644 --- a/src/Core/src/Eventuous.Serialization/IEventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization/IEventSerializer.cs @@ -4,19 +4,9 @@ namespace Eventuous; public interface IEventSerializer { - [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] DeserializationResult DeserializeEvent(ReadOnlySpan data, string eventType, string contentType); - [RequiresUnreferencedCode(SerializationUnreferencedCodeMessage)] - [RequiresDynamicCode(SerializationRequiresDynamicCodeMessage)] SerializationResult SerializeEvent(object evt); - - internal const string SerializationUnreferencedCodeMessage = - "JSON serialization and deserialization might require types that cannot be statically analyzed. Use DefaultStaticEventSerializer with System.Text.Json source generation for native AOT applications."; - - internal const string SerializationRequiresDynamicCodeMessage = - "JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use DefaultStaticEventSerializer with System.Text.Json source generation for native AOT applications."; } public record SerializationResult(string EventType, string ContentType, byte[] Payload); diff --git a/src/Core/src/Eventuous.Subscriptions/Constants.cs b/src/Core/src/Eventuous.Subscriptions/Constants.cs deleted file mode 100644 index e726cd758..000000000 --- a/src/Core/src/Eventuous.Subscriptions/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.Subscriptions; - -internal static class AttrConstants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/Core/src/Eventuous.Subscriptions/EventSubscription.cs b/src/Core/src/Eventuous.Subscriptions/EventSubscription.cs index 65b3b9028..911a990ca 100644 --- a/src/Core/src/Eventuous.Subscriptions/EventSubscription.cs +++ b/src/Core/src/Eventuous.Subscriptions/EventSubscription.cs @@ -11,7 +11,6 @@ namespace Eventuous.Subscriptions; -using System.Diagnostics.CodeAnalysis; using Context; using Diagnostics; using Filters; @@ -44,7 +43,7 @@ protected EventSubscription( LoggerFactory = loggerFactory; Pipe = Ensure.NotNull(consumePipe); - EventSerializer = eventSerializer ?? DefaultEventSerializer.Instance; + EventSerializer = eventSerializer ?? Eventuous.EventSerializer.Default; Options = options; Log = Logger.CreateContext(options.SubscriptionId, loggerFactory); } @@ -54,8 +53,6 @@ protected EventSubscription( public string SubscriptionId => Options.SubscriptionId; - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] public async ValueTask Subscribe(OnSubscribed onSubscribed, OnDropped onDropped, CancellationToken cancellationToken) { if (IsRunning) return; @@ -149,8 +146,6 @@ protected async ValueTask Handler(IMessageConsumeContext context) { } } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected object? DeserializeData(string eventContentType, string eventType, ReadOnlyMemory data, string stream, ulong position = 0) { if (data.IsEmpty) return null; @@ -181,15 +176,11 @@ protected async ValueTask Handler(IMessageConsumeContext context) { } // TODO: Passing the handler function would allow decoupling subscribers from handlers - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected abstract ValueTask Subscribe(CancellationToken cancellationToken); protected abstract ValueTask Unsubscribe(CancellationToken cancellationToken); [PublicAPI] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected virtual async Task Resubscribe(TimeSpan delay, CancellationToken cancellationToken) { await Task.Delay(delay, cancellationToken).NoContext(); @@ -210,8 +201,6 @@ protected virtual async Task Resubscribe(TimeSpan delay, CancellationToken cance } } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected void Dropped(DropReason reason, Exception? exception) { if (!IsRunning) return; diff --git a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs index 11469191a..6369bd2d6 100644 --- a/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs +++ b/src/Core/src/Eventuous.Subscriptions/EventSubscriptionWithCheckpoint.cs @@ -6,7 +6,6 @@ namespace Eventuous.Subscriptions; -using System.Diagnostics.CodeAnalysis; using Checkpoints; using Context; using Filters; @@ -53,8 +52,6 @@ EventPosition GetPositionFromContext(IMessageConsumeContext context) SubscriptionKind.Stream => EventPosition.FromContext(context) }; - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected async ValueTask HandleInternal(IMessageConsumeContext context) { try { Logger.Current = Log; @@ -76,8 +73,6 @@ protected async ValueTask HandleInternal(IMessageConsumeContext context) { /// that throw would silently kill the channel worker without triggering Dropped/Resubscribe. /// This wrapper catches the throw and calls Dropped instead. /// - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] ValueTask NackOnAsyncWorker(IMessageConsumeContext context, Exception exception) { try { return Nack(context, exception); @@ -127,8 +122,6 @@ protected async Task GetCheckpoint(CancellationToken cancellationTok return checkpoint; } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected override async Task Resubscribe(TimeSpan delay, CancellationToken cancellationToken) { // Reset checkpoint state so the new run reads from the committed checkpoint, // not from LastProcessed (which may be ahead of the failed event). diff --git a/src/Core/src/Eventuous.Subscriptions/IMessageSubscription.cs b/src/Core/src/Eventuous.Subscriptions/IMessageSubscription.cs index df49fc6f0..190c189db 100644 --- a/src/Core/src/Eventuous.Subscriptions/IMessageSubscription.cs +++ b/src/Core/src/Eventuous.Subscriptions/IMessageSubscription.cs @@ -14,8 +14,6 @@ namespace Eventuous.Subscriptions; public interface IMessageSubscription { string SubscriptionId { get; } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] ValueTask Subscribe(OnSubscribed onSubscribed, OnDropped onDropped, CancellationToken cancellationToken); ValueTask Unsubscribe(OnUnsubscribed onUnsubscribed, CancellationToken cancellationToken); diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj index 0805456be..bd962c0c0 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj @@ -17,5 +17,6 @@ + \ No newline at end of file diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj index 522d87cd3..c3b232d02 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj @@ -7,6 +7,7 @@ + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs new file mode 100644 index 000000000..73da5acf8 --- /dev/null +++ b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace Eventuous.Tests.Subscriptions; + +static class TestSetup { + [ModuleInitializer] + internal static void Initialize() + => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); +} diff --git a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj index 3bf2878d0..72a0f5394 100644 --- a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj +++ b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Experimental/src/ElasticPlayground/Program.cs b/src/Experimental/src/ElasticPlayground/Program.cs index 44fb7f498..aff819327 100644 --- a/src/Experimental/src/ElasticPlayground/Program.cs +++ b/src/Experimental/src/ElasticPlayground/Program.cs @@ -26,7 +26,7 @@ var kurrentDBClientSettings = KurrentDBClientSettings.Create("esdb://localhost:2113?tls=false"); var kurrentDBClient = new KurrentDBClient(kurrentDBClientSettings); -DefaultEventSerializer.SetDefaultSerializer(new DefaultEventSerializer(options)); +EventSerializer.SetDefault(new DefaultEventSerializer(options)); // var elasticOnly = new ElasticOnly(client); // await elasticOnly.Execute(); diff --git a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj index fc6727756..a9e8f82eb 100644 --- a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj @@ -9,6 +9,7 @@ + Analyzer false diff --git a/src/Extensions/test/Eventuous.Sut.AspNetCore/Program.cs b/src/Extensions/test/Eventuous.Sut.AspNetCore/Program.cs index 48b16b70b..28260a39c 100644 --- a/src/Extensions/test/Eventuous.Sut.AspNetCore/Program.cs +++ b/src/Extensions/test/Eventuous.Sut.AspNetCore/Program.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Http.Json; using BookingService = Eventuous.Sut.AspNetCore.BookingService; -DefaultEventSerializer.SetDefaultSerializer(new DefaultEventSerializer(TestPrimitives.DefaultOptions)); +EventSerializer.SetDefault(new DefaultEventSerializer(TestPrimitives.DefaultOptions)); var builder = WebApplication.CreateBuilder(args); builder.Services.AddCommandService(); diff --git a/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs b/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs index 7fb8d6779..0304bc5f0 100644 --- a/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs +++ b/src/GooglePubSub/src/Eventuous.GooglePubSub/Producers/GooglePubSubProducer.cs @@ -42,7 +42,7 @@ public GooglePubSubProducer( public GooglePubSubProducer(PubSubProducerOptions options, IEventSerializer? serializer = null, ILogger? log = null) : base(TracingOptions) { Ensure.NotNull(options); - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _clientCache = new(options, log); _attributes = options.Attributes; _log = log; diff --git a/src/Kafka/src/Eventuous.Kafka/Producers/KafkaBasicProducer.cs b/src/Kafka/src/Eventuous.Kafka/Producers/KafkaBasicProducer.cs index b6dbf82e5..6a0d00500 100644 --- a/src/Kafka/src/Eventuous.Kafka/Producers/KafkaBasicProducer.cs +++ b/src/Kafka/src/Eventuous.Kafka/Producers/KafkaBasicProducer.cs @@ -19,7 +19,7 @@ public KafkaBasicProducer(KafkaProducerOptions options, IEventSerializer? serial : base(TracingOptions) { _producerWithKey = new ProducerBuilder(options.ProducerConfig).Build(); _producerWithoutKey = new DependentProducerBuilder(_producerWithKey.Handle).Build(); - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; } static readonly ProducerTracingOptions TracingOptions = new() { diff --git a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs index 9ccda44ec..b7cc44b50 100644 --- a/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs +++ b/src/Kafka/test/Eventuous.Tests.Kafka/BasicProducerTests.cs @@ -70,7 +70,7 @@ async Task Consume(IConsumer c, CancellationToken ct) { var messageType = meta[KafkaHeaderKeys.MessageTypeHeader] as string; var contentType = meta[KafkaHeaderKeys.ContentTypeHeader] as string; - var result = DefaultEventSerializer.Instance.DeserializeEvent(msg.Message.Value, messageType!, contentType!) as SuccessfullyDeserialized; + var result = EventSerializer.Default.DeserializeEvent(msg.Message.Value, messageType!, contentType!) as SuccessfullyDeserialized; var evt = (result!.Payload as TestEvent)!; TestContext.Current?.OutputWriter.WriteLine($"Consumed {evt}"); diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/Constants.cs b/src/KurrentDB/src/Eventuous.KurrentDB/Constants.cs deleted file mode 100644 index 06eef81ad..000000000 --- a/src/KurrentDB/src/Eventuous.KurrentDB/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.KurrentDB; - -internal static class AttrConstants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/KurrentDBEventStore.cs b/src/KurrentDB/src/Eventuous.KurrentDB/KurrentDBEventStore.cs index 5deced054..d8cf4a269 100644 --- a/src/KurrentDB/src/Eventuous.KurrentDB/KurrentDBEventStore.cs +++ b/src/KurrentDB/src/Eventuous.KurrentDB/KurrentDBEventStore.cs @@ -79,7 +79,7 @@ public KurrentDBEventStore( ILogger? logger = null ) { _client = Ensure.NotNull(client); - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _metaSerializer = metaSerializer ?? DefaultMetadataSerializer.Instance; _logger = logger ?? NullLogger.Instance; } @@ -110,8 +110,6 @@ public async Task StreamExists(StreamName stream, CancellationToken cancel } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -140,8 +138,6 @@ public Task AppendEvents( } ); - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] EventData ToEventData(NewStreamEvent streamEvent) { var (eventType, contentType, payload) = _serializer.SerializeEvent(streamEvent.Payload!); @@ -156,8 +152,6 @@ EventData ToEventData(NewStreamEvent streamEvent) { } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public Task AppendEvents( IReadOnlyCollection appends, CancellationToken cancellationToken = default @@ -207,8 +201,6 @@ static async IAsyncEnumerable ToAsyncEnumerable(IEnumerable } } - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] EventData ToEventData(NewStreamEvent streamEvent) { var (eventType, contentType, payload) = _serializer.SerializeEvent(streamEvent.Payload!); @@ -223,8 +215,6 @@ EventData ToEventData(NewStreamEvent streamEvent) { } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var read = _client.ReadStreamAsync(Direction.Forwards, stream, start.AsStreamPosition(), count, cancellationToken: cancellationToken); @@ -244,8 +234,6 @@ public async IAsyncEnumerable ReadEvents(StreamName stream, StreamR } /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken = default) { var read = _client.ReadStreamAsync( Direction.Backwards, @@ -334,8 +322,6 @@ static StreamState ToStreamState(ExpectedStreamVersion version) : StreamState.StreamRevision((ulong)version.Value); [MethodImpl(MethodImplOptions.AggressiveInlining)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] StreamEvent? ToStreamEvent(ResolvedEvent resolvedEvent) { var deserialized = _serializer.DeserializeEvent( resolvedEvent.Event.Data.Span, @@ -375,8 +361,6 @@ StreamEvent AsStreamEvent(object payload) ); } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] StreamEvent[] ToStreamEvents(ResolvedEvent[] resolvedEvents) => resolvedEvents .Select(ToStreamEvent) diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/Producers/KurrentDBProducer.cs b/src/KurrentDB/src/Eventuous.KurrentDB/Producers/KurrentDBProducer.cs index fda3263cb..8399485fd 100644 --- a/src/KurrentDB/src/Eventuous.KurrentDB/Producers/KurrentDBProducer.cs +++ b/src/KurrentDB/src/Eventuous.KurrentDB/Producers/KurrentDBProducer.cs @@ -25,7 +25,7 @@ public class KurrentDBProducer : BaseProducer { public KurrentDBProducer(KurrentDBClient client, IEventSerializer? serializer = null, IMetadataSerializer? metaSerializer = null) : base(TracingOptions) { _client = Ensure.NotNull(client); - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _metaSerializer = metaSerializer ?? DefaultMetadataSerializer.Instance; } @@ -51,8 +51,6 @@ public KurrentDBProducer(KurrentDBClientSettings clientSettings, IEventSerialize /// Batch of messages /// Options for the produce operation /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] protected override async Task ProduceMessages( StreamName stream, IEnumerable messages, @@ -89,8 +87,6 @@ await chunkMessages } } - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] EventData CreateMessage(ProducedMessage message, bool setMessageType) { var msg = Ensure.NotNull(message.Message); var (eventType, contentType, payload) = _serializer.SerializeEvent(msg); diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/AllStreamSubscription.cs b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/AllStreamSubscription.cs index 6acf814a4..b689f845c 100644 --- a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/AllStreamSubscription.cs +++ b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/AllStreamSubscription.cs @@ -74,8 +74,6 @@ public AllStreamSubscription( /// Starts the subscription /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] protected override async ValueTask Subscribe(CancellationToken cancellationToken) { var filterOptions = new SubscriptionFilterOptions( Options.EventFilter ?? EventTypeFilter.ExcludeSystemEvents(), @@ -112,8 +110,6 @@ void HandleDrop(global::KurrentDB.Client.StreamSubscription _, SubscriptionDropp => Dropped(KurrentDBMappings.AsDropReason(reason), ex); } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] MessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancellationToken) { var evt = DeserializeData( re.Event.ContentType, diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/PersistentSubscriptionBase.cs b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/PersistentSubscriptionBase.cs index 691080504..6dc68865a 100644 --- a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/PersistentSubscriptionBase.cs +++ b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/PersistentSubscriptionBase.cs @@ -116,8 +116,6 @@ protected PersistentSubscriptionBase( /// Subscribe to a persistent subscription /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] protected override async ValueTask Subscribe(CancellationToken cancellationToken) { var settings = Options.SubscriptionSettings ?? new PersistentSubscriptionSettings(Options.ResolveLinkTos); @@ -194,8 +192,6 @@ async ValueTask Nack(MessageConsumeContext ctx, Exception exception) { await _handleEventProcessingFailure(Client, subscription, re, exception).NoContext(); } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] MessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancellationToken) { var evt = DeserializeData( re.Event.ContentType, diff --git a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/StreamSubscription.cs b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/StreamSubscription.cs index d9029e823..9254e5e4b 100644 --- a/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/StreamSubscription.cs +++ b/src/KurrentDB/src/Eventuous.KurrentDB/Subscriptions/StreamSubscription.cs @@ -86,8 +86,6 @@ public StreamSubscription( /// Starts a catch-up subscription /// /// - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] protected override async ValueTask Subscribe(CancellationToken cancellationToken) { var (_, position) = await GetCheckpoint(cancellationToken).NoContext(); @@ -128,8 +126,6 @@ void HandleDrop(global::KurrentDB.Client.StreamSubscription _, SubscriptionDropp => Dropped(KurrentDBMappings.AsDropReason(reason), ex); } - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] MessageConsumeContext CreateContext(ResolvedEvent re, CancellationToken cancellationToken) { var evt = DeserializeData( re.Event.ContentType, diff --git a/src/KurrentDB/test/Eventuous.Tests.KurrentDB/Subscriptions/CustomDependenciesTests.cs b/src/KurrentDB/test/Eventuous.Tests.KurrentDB/Subscriptions/CustomDependenciesTests.cs index 2d4b0edea..33b36317b 100644 --- a/src/KurrentDB/test/Eventuous.Tests.KurrentDB/Subscriptions/CustomDependenciesTests.cs +++ b/src/KurrentDB/test/Eventuous.Tests.KurrentDB/Subscriptions/CustomDependenciesTests.cs @@ -106,7 +106,7 @@ public ValueTask StoreCheckpoint(Checkpoint checkpoint, bool force, class TestSerializer : IEventSerializer { public DeserializationResult DeserializeEvent(ReadOnlySpan data, string eventType, string contentType) { - var result = DefaultEventSerializer.Instance.DeserializeEvent(data, eventType, contentType); + var result = EventSerializer.Default.DeserializeEvent(data, eventType, contentType); if (result is not DeserializationResult.SuccessfullyDeserialized { Payload: var evt }) { return result; diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj index ea0cab941..9f0c84165 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj @@ -11,6 +11,7 @@ + diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs index fa4c755a3..0c8f0e550 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Fixtures/IntegrationFixture.cs @@ -26,7 +26,7 @@ public Task AppendEvent(StreamName streamName, object evt, E ); static IntegrationFixture() { - DefaultEventSerializer.SetDefaultSerializer(Serializer); + EventSerializer.SetDefault(Serializer); NodaTimeSerializers.Register(); } diff --git a/src/RabbitMq/src/Eventuous.RabbitMq/Constants.cs b/src/RabbitMq/src/Eventuous.RabbitMq/Constants.cs deleted file mode 100644 index 477d5cbd2..000000000 --- a/src/RabbitMq/src/Eventuous.RabbitMq/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.RabbitMq; - -internal static class AttrConstants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/RabbitMq/src/Eventuous.RabbitMq/Producers/RabbitMqProducer.cs b/src/RabbitMq/src/Eventuous.RabbitMq/Producers/RabbitMqProducer.cs index 4f9bcd880..275d62840 100644 --- a/src/RabbitMq/src/Eventuous.RabbitMq/Producers/RabbitMqProducer.cs +++ b/src/RabbitMq/src/Eventuous.RabbitMq/Producers/RabbitMqProducer.cs @@ -40,7 +40,7 @@ public RabbitMqProducer( : base(TracingOptions) { _log = log; _options = options; - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _connectionFactory = Ensure.NotNull(connectionFactory); _exchangeCache = new(_log); } @@ -60,8 +60,6 @@ public Task StartAsync(CancellationToken cancellationToken = default) { ProduceOperation = "publish" }; - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected override async Task ProduceMessages( StreamName stream, IEnumerable messages, @@ -96,8 +94,6 @@ await failed .NoContext(); } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] void Publish(string stream, ProducedMessage message, RabbitMqProduceOptions? options) { if (_channel == null) throw new InvalidOperationException("Producer hasn't been initialized, call Initialize"); diff --git a/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs b/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs index df39635c7..1b2bd7379 100644 --- a/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs +++ b/src/RabbitMq/src/Eventuous.RabbitMq/Subscriptions/RabbitMqSubscription.cs @@ -93,8 +93,6 @@ public RabbitMqSubscription( eventSerializer ) { } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] protected override ValueTask Subscribe(CancellationToken cancellationToken) { var exchange = Ensure.NotEmptyString(Options.Exchange); @@ -142,8 +140,6 @@ protected override ValueTask Subscribe(CancellationToken cancellationToken) { const string ReceivedMessageKey = "receivedMessage"; - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] async Task HandleReceived(object sender, BasicDeliverEventArgs received) { Logger.Current = Log; @@ -172,8 +168,6 @@ ValueTask Nack(IMessageConsumeContext ctx, Exception exception) { return default; } - [RequiresUnreferencedCode(AttrConstants.DynamicSerializationMessage)] - [RequiresDynamicCode(AttrConstants.DynamicSerializationMessage)] MessageConsumeContext CreateContext(object sender, BasicDeliverEventArgs received) { var evt = DeserializeData(received.BasicProperties.ContentType, received.BasicProperties.Type, received.Body, received.Exchange); diff --git a/src/Redis/src/Eventuous.Redis/RedisStore.cs b/src/Redis/src/Eventuous.Redis/RedisStore.cs index 7a9cd2f23..a4a733693 100644 --- a/src/Redis/src/Eventuous.Redis/RedisStore.cs +++ b/src/Redis/src/Eventuous.Redis/RedisStore.cs @@ -29,7 +29,7 @@ public RedisStore( IEventSerializer? serializer = null, IMetadataSerializer? metaSerializer = null ) { - _serializer = serializer ?? DefaultEventSerializer.Instance; + _serializer = serializer ?? EventSerializer.Default; _metaSerializer = metaSerializer ?? DefaultMetadataSerializer.Instance; _getDatabase = Ensure.NotNull(getDatabase, "Connection factory"); } diff --git a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj index 78daa2ab3..04ed76e85 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj +++ b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj @@ -16,5 +16,6 @@ + diff --git a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs index a4600945d..4e0ec7ea5 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs +++ b/src/Redis/test/Eventuous.Tests.Redis/Fixtures/IntegrationFixture.cs @@ -18,7 +18,7 @@ public sealed class IntegrationFixture : IAsyncInitializer, IAsyncDisposable { IEventSerializer Serializer { get; } = new DefaultEventSerializer(TestPrimitives.DefaultOptions); - public IntegrationFixture() => DefaultEventSerializer.SetDefaultSerializer(Serializer); + public IntegrationFixture() => EventSerializer.SetDefault(Serializer); public async Task InitializeAsync() { _redisContainer = new RedisBuilder().WithImage("redis:7.0.12-alpine").Build(); diff --git a/src/Relational/src/Eventuous.Sql.Base/Constants.cs b/src/Relational/src/Eventuous.Sql.Base/Constants.cs deleted file mode 100644 index 932f86c6f..000000000 --- a/src/Relational/src/Eventuous.Sql.Base/Constants.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (C) Eventuous HQ OÜ. All rights reserved -// Licensed under the Apache License, Version 2.0. - -namespace Eventuous.Sql.Base; - -internal static class Constants { - internal const string DynamicSerializationMessage = "Only works with AOT when using DefaultStaticEventSerializer"; -} diff --git a/src/Relational/src/Eventuous.Sql.Base/Producers/UniversalProducer.cs b/src/Relational/src/Eventuous.Sql.Base/Producers/UniversalProducer.cs index 66946706e..efe2877e8 100644 --- a/src/Relational/src/Eventuous.Sql.Base/Producers/UniversalProducer.cs +++ b/src/Relational/src/Eventuous.Sql.Base/Producers/UniversalProducer.cs @@ -11,8 +11,6 @@ namespace Eventuous.Sql.Base.Producers; /// public class UniversalProducer(IEventStore store) : IProducer { /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] public async Task Produce(StreamName stream, IEnumerable messages, CancellationToken cancellationToken = default) { var events = messages.Select(ToStreamEvent).ToList(); await store.AppendEvents(stream, ExpectedStreamVersion.Any, events, cancellationToken); diff --git a/src/Relational/src/Eventuous.Sql.Base/SqlEventStoreBase.cs b/src/Relational/src/Eventuous.Sql.Base/SqlEventStoreBase.cs index 1c9870e3d..e093be1a0 100644 --- a/src/Relational/src/Eventuous.Sql.Base/SqlEventStoreBase.cs +++ b/src/Relational/src/Eventuous.Sql.Base/SqlEventStoreBase.cs @@ -19,7 +19,7 @@ namespace Eventuous.Sql.Base; /// Database transaction type public abstract class SqlEventStoreBase(IEventSerializer? serializer, IMetadataSerializer? metaSerializer) : IEventStore where TConnection : DbConnection where TTransaction : DbTransaction { - protected IEventSerializer Serializer { get; } = serializer ?? DefaultEventSerializer.Instance; + protected IEventSerializer Serializer { get; } = serializer ?? EventSerializer.Default; protected IMetadataSerializer MetaSerializer { get; } = metaSerializer ?? DefaultMetadataSerializer.Instance; const string ContentType = "application/json"; @@ -91,8 +91,6 @@ StreamTruncatePosition position ); /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEvents(StreamName stream, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken) { if (count <= 0 || start == StreamReadPosition.End) yield break; @@ -102,8 +100,6 @@ public async IAsyncEnumerable ReadEvents(StreamName stream, StreamR } /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] public async IAsyncEnumerable ReadEventsBackwards(StreamName stream, StreamReadPosition start, int count, [EnumeratorCancellation] CancellationToken cancellationToken) { if (count <= 0) yield break; @@ -112,8 +108,6 @@ public async IAsyncEnumerable ReadEventsBackwards(StreamName stream foreach (var evt in events) yield return evt; } - [RequiresDynamicCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] - [RequiresUnreferencedCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] async Task ReadInternal(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) { await using var connection = await OpenConnection(cancellationToken).NoContext(); await using var cmd = GetReadCommand(connection, stream, start, count); @@ -121,8 +115,6 @@ async Task ReadInternal(StreamName stream, StreamReadPosition sta return await ReadFromCommand(cmd, stream, cancellationToken).NoContext(); } - [RequiresDynamicCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] - [RequiresUnreferencedCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] async Task ReadInternalBackwards(StreamName stream, StreamReadPosition start, int count, CancellationToken cancellationToken) { await using var connection = await OpenConnection(cancellationToken).NoContext(); await using var cmd = GetReadBackwardsCommand(connection, stream, start, count); @@ -130,8 +122,6 @@ async Task ReadInternalBackwards(StreamName stream, StreamReadPos return await ReadFromCommand(cmd, stream, cancellationToken).NoContext(); } - [RequiresDynamicCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] - [RequiresUnreferencedCode("Calls Eventuous.Sql.Base.SqlEventStoreBase.ToStreamEvent(PersistedEvent)")] async Task ReadFromCommand(DbCommand cmd, StreamName stream, CancellationToken cancellationToken) { try { await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).NoContext(); @@ -146,8 +136,6 @@ async Task ReadFromCommand(DbCommand cmd, StreamName stream, Canc } } - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.DeserializeEvent(ReadOnlySpan, String, String)")] - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.DeserializeEvent(ReadOnlySpan, String, String)")] StreamEvent ToStreamEvent(PersistedEvent evt) { var deserialized = Serializer.DeserializeEvent(Encoding.UTF8.GetBytes(evt.JsonData), evt.MessageType, ContentType); @@ -163,8 +151,6 @@ StreamEvent ToStreamEvent(PersistedEvent evt) { } /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] public virtual async Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -195,8 +181,6 @@ CancellationToken cancellationToken throw IsConflict(e) ? new AppendToStreamException(stream, e) : e; } - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] NewPersistedEvent Convert(NewStreamEvent evt) { var data = Serializer.SerializeEvent(evt.Payload!); var meta = MetaSerializer.Serialize(evt.Metadata); @@ -208,8 +192,6 @@ NewPersistedEvent Convert(NewStreamEvent evt) { } /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] public virtual async Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) { if (appends.Count == 0) return []; @@ -242,8 +224,6 @@ public virtual async Task AppendEvents(IReadOnlyCollection throw IsConflict(e) ? new AppendToStreamException(streamNames, e) : e; } - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] NewPersistedEvent Convert(NewStreamEvent evt) { var data = Serializer.SerializeEvent(evt.Payload!); var meta = MetaSerializer.Serialize(evt.Metadata); diff --git a/src/Relational/src/Eventuous.Sql.Base/Subscriptions/SqlSubscriptionBase.cs b/src/Relational/src/Eventuous.Sql.Base/Subscriptions/SqlSubscriptionBase.cs index f63bc6830..6659b4086 100644 --- a/src/Relational/src/Eventuous.Sql.Base/Subscriptions/SqlSubscriptionBase.cs +++ b/src/Relational/src/Eventuous.Sql.Base/Subscriptions/SqlSubscriptionBase.cs @@ -76,8 +76,6 @@ public abstract class SqlSubscriptionBase( private record DetectedGap(long Position, DateTime FirstSeen); - [RequiresUnreferencedCode("Calls ExecutePollCycle()")] - [RequiresDynamicCode("Calls ExecutePollCycle()")] async Task PollingQuery(ulong? position, CancellationToken cancellationToken) { var start = position.HasValue ? (long)position : -1; @@ -94,8 +92,6 @@ async Task PollingQuery(ulong? position, CancellationToken cancellationToken) { return; - [RequiresUnreferencedCode("Calls Eventuous.Sql.Base.Subscriptions.SqlSubscriptionBase.ToConsumeContext(PersistedEvent, CancellationToken)")] - [RequiresDynamicCode("Calls Eventuous.Sql.Base.Subscriptions.SqlSubscriptionBase.ToConsumeContext(PersistedEvent, CancellationToken)")] async Task Poll() { try { await using var connection = await OpenConnection(cancellationToken).NoContext(); @@ -153,8 +149,6 @@ async Task Poll() { } } - [RequiresDynamicCode("Calls Poll()")] - [RequiresUnreferencedCode("Calls Poll()")] async Task ExecutePollCycle() { while (!cancellationToken.IsCancellationRequested) { var result = await Poll().NoContext(); @@ -208,8 +202,6 @@ async Task ExecutePollCycle() { /// Starts the subscription /// /// - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] protected override async ValueTask Subscribe(CancellationToken cancellationToken) { await BeforeSubscribe(cancellationToken).NoContext(); var (_, position) = await GetCheckpoint(cancellationToken).NoContext(); @@ -250,8 +242,6 @@ protected override async ValueTask Unsubscribe(CancellationToken cancellationTok SubscriptionKind.Stream => evt.StreamPosition }; - [RequiresDynamicCode(Constants.DynamicSerializationMessage)] - [RequiresUnreferencedCode(Constants.DynamicSerializationMessage)] MessageConsumeContext ToConsumeContext(PersistedEvent evt, CancellationToken cancellationToken) { Logger.Current = Log; diff --git a/src/Sqlite/src/Eventuous.Sqlite/SqliteStore.cs b/src/Sqlite/src/Eventuous.Sqlite/SqliteStore.cs index c93565ea0..010fe1da0 100644 --- a/src/Sqlite/src/Eventuous.Sqlite/SqliteStore.cs +++ b/src/Sqlite/src/Eventuous.Sqlite/SqliteStore.cs @@ -74,8 +74,6 @@ NewPersistedEvent[] events ) => throw new NotSupportedException("SQLite does not use GetAppendCommand. AppendEvents is overridden directly."); - [RequiresDynamicCode("Only works with AOT when using DefaultStaticEventSerializer")] - [RequiresUnreferencedCode("Only works with AOT when using DefaultStaticEventSerializer")] public override async Task AppendEvents( StreamName stream, ExpectedStreamVersion expectedVersion, @@ -107,8 +105,6 @@ CancellationToken cancellationToken } /// - [RequiresDynamicCode("Only works with AOT when using DefaultStaticEventSerializer")] - [RequiresUnreferencedCode("Only works with AOT when using DefaultStaticEventSerializer")] public override async Task AppendEvents(IReadOnlyCollection appends, CancellationToken cancellationToken) { if (appends.Count == 0) return []; @@ -230,8 +226,6 @@ RETURNING global_position return new((ulong)lastGlobalPosition, newVersion); } - [RequiresUnreferencedCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] - [RequiresDynamicCode("Calls Eventuous.IEventSerializer.SerializeEvent(Object)")] NewPersistedEvent Convert(NewStreamEvent evt) { var data = Serializer.SerializeEvent(evt.Payload!); var meta = MetaSerializer.Serialize(evt.Metadata); diff --git a/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj b/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj index 7b1670dd0..9a27272dc 100644 --- a/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj +++ b/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj @@ -20,5 +20,6 @@ + From 9bfc2ecb9e33361a3e9dcb7be64e85d57504cd94 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:30:56 +0100 Subject: [PATCH 2/7] fix: address PR review feedback for AOT serializer cleanup - Add TrySetDefault to EventSerializer using Interlocked.CompareExchange so DefaultEventSerializer constructor doesn't overwrite an existing serializer configuration - Add module initializer to Eventuous.Tests.Subscriptions.Base so integration tests (Azure, Kafka, KurrentDB) have EventSerializer.Default configured before use Co-Authored-By: Claude Opus 4.6 (1M context) --- .../DefaultEventSerializer.cs | 2 +- .../src/Eventuous.Serialization/EventSerializer.cs | 10 ++++++++++ .../Eventuous.Tests.Subscriptions.Base.csproj | 1 + .../Eventuous.Tests.Subscriptions.Base/TestSetup.cs | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs diff --git a/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs b/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs index 03c9a5515..a3ccd5b9a 100644 --- a/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs @@ -16,7 +16,7 @@ public DefaultEventSerializer(JsonSerializerOptions options, ITypeMapper? typeMa _typeMapper = typeMapper ?? TypeMap.Instance; // Auto-register as default if none is set - EventSerializer.SetDefault(this); + EventSerializer.TrySetDefault(this); } [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This class is not intended for AOT use.")] diff --git a/src/Core/src/Eventuous.Serialization/EventSerializer.cs b/src/Core/src/Eventuous.Serialization/EventSerializer.cs index c5f8b6f7d..7d33f9c86 100644 --- a/src/Core/src/Eventuous.Serialization/EventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization/EventSerializer.cs @@ -26,4 +26,14 @@ public static class EventSerializer { /// public static void SetDefault(IEventSerializer serializer) => _default = serializer ?? throw new ArgumentNullException(nameof(serializer)); + + /// + /// Sets the default event serializer only if one has not already been configured. + /// Returns true if the default was set, false if it was already configured. + /// + public static bool TrySetDefault(IEventSerializer serializer) { + ArgumentNullException.ThrowIfNull(serializer); + + return Interlocked.CompareExchange(ref _default, serializer, null) == null; + } } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj index 6647c4590..6ccea5d92 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj @@ -6,6 +6,7 @@ + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs new file mode 100644 index 000000000..4048e28ba --- /dev/null +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace Eventuous.Tests.Subscriptions.Base; + +static class TestSetup { + [ModuleInitializer] + internal static void Initialize() + => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); +} From c01d822078e1706275948a0634f3b096cc98ac81 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:33:26 +0100 Subject: [PATCH 3/7] fix: add serializer setup to Gateway and Azure ServiceBus tests These test projects use EventSerializer.Default without configuring it. Add module initializers with DefaultEventSerializer to fix CI failures. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs | 10 ++++++++++ .../Eventuous.Tests.Gateway.csproj | 1 + src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs | 10 ++++++++++ 3 files changed, 21 insertions(+) create mode 100644 src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs create mode 100644 src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs new file mode 100644 index 000000000..dfbee254b --- /dev/null +++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace Eventuous.Tests.Azure.ServiceBus; + +static class TestSetup { + [ModuleInitializer] + internal static void Initialize() + => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); +} diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj index bd8a6f1ab..7616c319f 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj +++ b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj @@ -5,6 +5,7 @@ + diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs new file mode 100644 index 000000000..582aad250 --- /dev/null +++ b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs @@ -0,0 +1,10 @@ +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace Eventuous.Tests.Gateway; + +static class TestSetup { + [ModuleInitializer] + internal static void Initialize() + => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); +} From 2bf59039694bd9b8515b1f2865ce8e751a8bef37 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:41:49 +0100 Subject: [PATCH 4/7] refactor: make test serializer setup explicit Use EventSerializer.SetDefault() explicitly instead of relying on the constructor's hidden TrySetDefault side effect. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs | 2 +- src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs | 2 +- src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs | 2 +- src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs index dfbee254b..b603835f3 100644 --- a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs +++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs @@ -6,5 +6,5 @@ namespace Eventuous.Tests.Azure.ServiceBus; static class TestSetup { [ModuleInitializer] internal static void Initialize() - => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); + => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs index 4048e28ba..dfc42a2a2 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs @@ -6,5 +6,5 @@ namespace Eventuous.Tests.Subscriptions.Base; static class TestSetup { [ModuleInitializer] internal static void Initialize() - => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); + => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs index 73da5acf8..354191dd4 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs @@ -6,5 +6,5 @@ namespace Eventuous.Tests.Subscriptions; static class TestSetup { [ModuleInitializer] internal static void Initialize() - => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); + => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs index 582aad250..eecec1daf 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs +++ b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs @@ -6,5 +6,5 @@ namespace Eventuous.Tests.Gateway; static class TestSetup { [ModuleInitializer] internal static void Initialize() - => new DefaultEventSerializer(new JsonSerializerOptions(JsonSerializerDefaults.Web)); + => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } From e6561ea866392d261a661958c34b6bba8a859eab Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:43:24 +0100 Subject: [PATCH 5/7] fix: suppress CA2255 warning on test module initializers Co-Authored-By: Claude Opus 4.6 (1M context) --- src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs | 2 ++ src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs | 2 ++ src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs | 2 ++ src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs index b603835f3..65c9d9263 100644 --- a/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs +++ b/src/Azure/test/Eventuous.Tests.Azure.ServiceBus/TestSetup.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Json; @@ -5,6 +6,7 @@ namespace Eventuous.Tests.Azure.ServiceBus; static class TestSetup { [ModuleInitializer] + [SuppressMessage("Usage", "CA2255")] internal static void Initialize() => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs index dfc42a2a2..c2edd8673 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/TestSetup.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Json; @@ -5,6 +6,7 @@ namespace Eventuous.Tests.Subscriptions.Base; static class TestSetup { [ModuleInitializer] + [SuppressMessage("Usage", "CA2255")] internal static void Initialize() => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs index 354191dd4..c48f06b1c 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs +++ b/src/Core/test/Eventuous.Tests.Subscriptions/TestSetup.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Json; @@ -5,6 +6,7 @@ namespace Eventuous.Tests.Subscriptions; static class TestSetup { [ModuleInitializer] + [SuppressMessage("Usage", "CA2255")] internal static void Initialize() => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs index eecec1daf..9d47ce94c 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs +++ b/src/Gateway/test/Eventuous.Tests.Gateway/TestSetup.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.Json; @@ -5,6 +6,7 @@ namespace Eventuous.Tests.Gateway; static class TestSetup { [ModuleInitializer] + [SuppressMessage("Usage", "CA2255")] internal static void Initialize() => EventSerializer.SetDefault(new DefaultEventSerializer(new(JsonSerializerDefaults.Web))); } From bb8a98914170905d052cad02e6c9564141cda2ac Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 18:59:49 +0100 Subject: [PATCH 6/7] rename: Eventuous.Serialization.Json -> Eventuous.Serialization.Json.Dynamic The old name was ambiguous since DefaultStaticEventSerializer also uses System.Text.Json. The new name clarifies this package contains the reflection-based (non-AOT) dynamic serializer. Co-Authored-By: Claude Opus 4.6 (1M context) --- Eventuous.slnx | 2 +- samples/kurrentdb/Bookings/Bookings.csproj | 2 +- samples/postgres/Bookings/Bookings.csproj | 2 +- .../DefaultEventSerializer.cs | 4 ---- .../Eventuous.Serialization.Json.Dynamic.csproj} | 0 .../Eventuous.Tests.Persistence.Base.csproj | 2 +- .../Eventuous.Tests.Subscriptions.Base.csproj | 2 +- .../Eventuous.Tests.Subscriptions.csproj | 2 +- .../src/ElasticPlayground/ElasticPlayground.csproj | 2 +- .../Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj | 2 +- .../Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj | 2 +- .../Eventuous.Tests.Projections.MongoDB.csproj | 2 +- .../test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj | 2 +- .../test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj | 2 +- 14 files changed, 12 insertions(+), 16 deletions(-) rename src/Core/src/{Eventuous.Serialization.Json => Eventuous.Serialization.Json.Dynamic}/DefaultEventSerializer.cs (77%) rename src/Core/src/{Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj => Eventuous.Serialization.Json.Dynamic/Eventuous.Serialization.Json.Dynamic.csproj} (100%) diff --git a/Eventuous.slnx b/Eventuous.slnx index c564925de..c10e88858 100644 --- a/Eventuous.slnx +++ b/Eventuous.slnx @@ -52,7 +52,7 @@ - + diff --git a/samples/kurrentdb/Bookings/Bookings.csproj b/samples/kurrentdb/Bookings/Bookings.csproj index 13246f694..2d46cb2c6 100644 --- a/samples/kurrentdb/Bookings/Bookings.csproj +++ b/samples/kurrentdb/Bookings/Bookings.csproj @@ -35,7 +35,7 @@ - + diff --git a/samples/postgres/Bookings/Bookings.csproj b/samples/postgres/Bookings/Bookings.csproj index 6e82ed4fa..4ef641f59 100644 --- a/samples/postgres/Bookings/Bookings.csproj +++ b/samples/postgres/Bookings/Bookings.csproj @@ -31,6 +31,6 @@ - + \ No newline at end of file diff --git a/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs b/src/Core/src/Eventuous.Serialization.Json.Dynamic/DefaultEventSerializer.cs similarity index 77% rename from src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs rename to src/Core/src/Eventuous.Serialization.Json.Dynamic/DefaultEventSerializer.cs index a3ccd5b9a..79ebe612e 100644 --- a/src/Core/src/Eventuous.Serialization.Json/DefaultEventSerializer.cs +++ b/src/Core/src/Eventuous.Serialization.Json.Dynamic/DefaultEventSerializer.cs @@ -19,8 +19,6 @@ public DefaultEventSerializer(JsonSerializerOptions options, ITypeMapper? typeMa EventSerializer.TrySetDefault(this); } - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This class is not intended for AOT use.")] - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This class is not intended for AOT use.")] public DeserializationResult DeserializeEvent(ReadOnlySpan data, string eventType, string contentType) { var typeMapped = _typeMapper.TryGetType(eventType, out var dataType); @@ -34,8 +32,6 @@ public DeserializationResult DeserializeEvent(ReadOnlySpan data, string ev : new FailedToDeserialize(DeserializationError.PayloadEmpty); } - [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This class is not intended for AOT use.")] - [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "This class is not intended for AOT use.")] public SerializationResult SerializeEvent(object evt) => new(_typeMapper.GetTypeName(evt), ContentType, JsonSerializer.SerializeToUtf8Bytes(evt, _options)); diff --git a/src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj b/src/Core/src/Eventuous.Serialization.Json.Dynamic/Eventuous.Serialization.Json.Dynamic.csproj similarity index 100% rename from src/Core/src/Eventuous.Serialization.Json/Eventuous.Serialization.Json.csproj rename to src/Core/src/Eventuous.Serialization.Json.Dynamic/Eventuous.Serialization.Json.Dynamic.csproj diff --git a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj index bd962c0c0..e7f6fe565 100644 --- a/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Persistence.Base/Eventuous.Tests.Persistence.Base.csproj @@ -17,6 +17,6 @@ - + \ No newline at end of file diff --git a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj index 6ccea5d92..5bdf142bd 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions.Base/Eventuous.Tests.Subscriptions.Base.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj index c3b232d02..673f6ca15 100644 --- a/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj +++ b/src/Core/test/Eventuous.Tests.Subscriptions/Eventuous.Tests.Subscriptions.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj index 72a0f5394..1853557a0 100644 --- a/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj +++ b/src/Experimental/src/ElasticPlayground/ElasticPlayground.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj index a9e8f82eb..28c6664ed 100644 --- a/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj +++ b/src/Extensions/test/Eventuous.Sut.AspNetCore/Eventuous.Sut.AspNetCore.csproj @@ -9,7 +9,7 @@ - + Analyzer false diff --git a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj index 7616c319f..b078e1220 100644 --- a/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj +++ b/src/Gateway/test/Eventuous.Tests.Gateway/Eventuous.Tests.Gateway.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj index 9f0c84165..5aa7fd95a 100644 --- a/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj +++ b/src/Mongo/test/Eventuous.Tests.Projections.MongoDB/Eventuous.Tests.Projections.MongoDB.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj index 04ed76e85..afd7d48da 100644 --- a/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj +++ b/src/Redis/test/Eventuous.Tests.Redis/Eventuous.Tests.Redis.csproj @@ -16,6 +16,6 @@ - + diff --git a/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj b/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj index 9a27272dc..b8ca40d56 100644 --- a/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj +++ b/src/Sqlite/test/Eventuous.Tests.Sqlite/Eventuous.Tests.Sqlite.csproj @@ -20,6 +20,6 @@ - + From f1dae7240b193e30ac980c6c969959e9940642c4 Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 13 Mar 2026 19:01:17 +0100 Subject: [PATCH 7/7] docs: update serialization docs for AOT-compatible serializer changes Rewrite the default serializer section to document both DefaultStaticEventSerializer (AOT, recommended) and DefaultEventSerializer (reflection-based, separate package). Update API references from the old DefaultEventSerializer.SetDefaultSerializer to the new EventSerializer.SetDefault. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../docs/next/persistence/serialisation.md | 45 +++++++++++++++---- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/docs/src/content/docs/next/persistence/serialisation.md b/docs/src/content/docs/next/persistence/serialisation.md index 9ca3b2bc1..19fbf7eb6 100644 --- a/docs/src/content/docs/next/persistence/serialisation.md +++ b/docs/src/content/docs/next/persistence/serialisation.md @@ -92,29 +92,58 @@ With the source generator in place, calling `RegisterKnownEventTypes()` is typic ### Default serializer -Eventuous provides a default serializer implementation, which uses `System.Text.Json`. You just need to register it in the `Startup` to make it available for the infrastructure components, like [aggregate store](../aggregate-store) and [subscriptions](../../subscriptions/subs-concept). +Eventuous provides two built-in serializer implementations, both using `System.Text.Json`: -Normally, you don't need to register or provide the serializer instance to any of the Eventuous classes that perform serialization and deserialization work. It's because they will use the default serializer instance instead. +- **`DefaultStaticEventSerializer`** — uses a `JsonSerializerContext` for source-generated, AOT-compatible serialization. This is the recommended choice for new applications and is required for Native AOT. +- **`DefaultEventSerializer`** — uses reflection-based serialization. Available in the `Eventuous.Serialization.Json.Dynamic` package. -However, you can register the default serializer with different options, or a custom serializer instead: +The `IEventSerializer` interface itself has no AOT restrictions, so custom serializer implementations work cleanly in AOT scenarios. + +#### AOT-compatible serializer (recommended) + +For AOT applications, use `DefaultStaticEventSerializer` with a `JsonSerializerContext`: ```csharp title="Program.cs" -builder.Services.AddSingleton( +// Define a JsonSerializerContext with your event types +[JsonSerializable(typeof(RoomBooked))] +[JsonSerializable(typeof(BookingPaid))] +[JsonSerializable(typeof(BookingCancelled))] +public partial class BookingSerializerContext : JsonSerializerContext; + +// Register at startup +EventSerializer.SetDefault( + new DefaultStaticEventSerializer(BookingSerializerContext.Default) +); +``` + +#### Reflection-based serializer + +For non-AOT applications, the `DefaultEventSerializer` from the `Eventuous.Serialization.Json.Dynamic` package provides a simpler setup that doesn't require a `JsonSerializerContext`: + +```csharp title="Program.cs" +EventSerializer.SetDefault( new DefaultEventSerializer( new JsonSerializerOptions(JsonSerializerDefaults.Default) ) ); ``` -You might want to avoid registering the serializer and override the one that Eventuous uses as the default instance: +:::caution +`DefaultEventSerializer` uses reflection-based JSON serialization and is not compatible with Native AOT. If you plan to publish your application as AOT, use `DefaultStaticEventSerializer` instead. +::: + +#### Configuring via DI + +You can also register the serializer in the DI container: ```csharp title="Program.cs" -var defaultSerializer = new DefaultEventSerializer( - new JsonSerializerOptions(JsonSerializerDefaults.Default) +builder.Services.AddSingleton( + new DefaultStaticEventSerializer(BookingSerializerContext.Default) ); -DefaultEventSerializer.SetDefaultSerializer(serializer); ``` +Infrastructure components like event stores, subscriptions, and producers will resolve `IEventSerializer` from DI when available. If no serializer is registered in DI, they fall back to `EventSerializer.Default`, which must be configured explicitly at startup. + ### Metadata serializer In many cases you might want to store event metadata in addition to the event payload. Normally, you'd use the same way to serialize both the event payload and its metadata, but it's not always the case. For example, you might store your events in Protobuf, but keep metadata as JSON.