From c03cb96176b8b144e18c0d801b77da653a1d347d Mon Sep 17 00:00:00 2001 From: mrmorrandir <43349286+mrmorrandir@users.noreply.github.com> Date: Mon, 13 May 2024 07:10:22 +0200 Subject: [PATCH] BugFix: DependencyInjection for mapping and projections. --- .../EventSourcing.Abstractions.csproj | 4 +-- .../EventSourcing.Publishers.RabbitMQ.csproj | 4 +-- .../EventSourcing.Publishers.csproj | 4 +-- src/EventSourcing/DI/EventMappingOptions.cs | 3 ++ .../DI/EventMappingOptionsBuilder.cs | 28 +++++++------------ .../DI/EventProjectionOptions.cs | 3 ++ .../DI/EventProjectionOptionsBuilder.cs | 19 ++++++------- .../DI/EventSourcingOptionsBuilder.cs | 24 ++++++++++------ src/EventSourcing/EventSourcing.csproj | 17 +++++------ .../EventSourcing.Benchmarks.csproj | 7 +++-- ...ng.FunctionTests.DI.InvalidAssembly.csproj | 2 +- ...g.FunctionTests.DI.InvalidAssembly2.csproj | 2 +- ...cing.FunctionTests.DI.ValidAssembly.csproj | 2 +- .../DependencyInjectionTests.cs | 2 ++ .../EventSourcing.FunctionTests.DI.csproj | 7 +++-- .../EventSourcing.FunctionTests.csproj | 7 +++-- .../Mappers/EventRegistryTests.cs | 7 +++++ ...ublishers.RabbitMQ.IntegrationTests.csproj | 7 +++-- 18 files changed, 84 insertions(+), 65 deletions(-) create mode 100644 src/EventSourcing/DI/EventMappingOptions.cs create mode 100644 src/EventSourcing/DI/EventProjectionOptions.cs diff --git a/src/EventSourcing.Abstractions/EventSourcing.Abstractions.csproj b/src/EventSourcing.Abstractions/EventSourcing.Abstractions.csproj index 0d643c0..cf3088b 100644 --- a/src/EventSourcing.Abstractions/EventSourcing.Abstractions.csproj +++ b/src/EventSourcing.Abstractions/EventSourcing.Abstractions.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable 1.0.0-beta-2 @@ -14,7 +14,7 @@ git EventSourcing, Events, EventStore true - 1.0.0-beta-6 + 1.0.0-preview-20240513-0708 Braking Changes - New Mapper Logic EventSourcing diff --git a/src/EventSourcing.Publishers.RabbitMQ/EventSourcing.Publishers.RabbitMQ.csproj b/src/EventSourcing.Publishers.RabbitMQ/EventSourcing.Publishers.RabbitMQ.csproj index 15de237..811ecd9 100644 --- a/src/EventSourcing.Publishers.RabbitMQ/EventSourcing.Publishers.RabbitMQ.csproj +++ b/src/EventSourcing.Publishers.RabbitMQ/EventSourcing.Publishers.RabbitMQ.csproj @@ -1,10 +1,10 @@ - net6.0 + net8.0 enable enable - 1.0.0-beta006 + 1.0.0-preview-20240513-0708 EventSourcing.Publishers.RabbitMQ Andreas Naumann Extend the event sourcing system with RabbitMQ. diff --git a/src/EventSourcing.Publishers/EventSourcing.Publishers.csproj b/src/EventSourcing.Publishers/EventSourcing.Publishers.csproj index cc5758a..aa540ad 100644 --- a/src/EventSourcing.Publishers/EventSourcing.Publishers.csproj +++ b/src/EventSourcing.Publishers/EventSourcing.Publishers.csproj @@ -1,10 +1,10 @@ - net6.0 + net8.0 enable enable - 1.0.0-beta006 + 1.0.0-preview-20240513-0708 EventSourcing.Publishers Andreas Naumann Extend the event sourcing system with publishers like Mqtt or RabbitMQ diff --git a/src/EventSourcing/DI/EventMappingOptions.cs b/src/EventSourcing/DI/EventMappingOptions.cs new file mode 100644 index 0000000..7fd772b --- /dev/null +++ b/src/EventSourcing/DI/EventMappingOptions.cs @@ -0,0 +1,3 @@ +namespace Microsoft.Extensions.DependencyInjection; + +public record EventMappingOptions(IServiceCollection Services, IEnumerable CoveredEvents, IEnumerable? UncoveredEvents = null); \ No newline at end of file diff --git a/src/EventSourcing/DI/EventMappingOptionsBuilder.cs b/src/EventSourcing/DI/EventMappingOptionsBuilder.cs index 7d38ac7..533aa99 100644 --- a/src/EventSourcing/DI/EventMappingOptionsBuilder.cs +++ b/src/EventSourcing/DI/EventMappingOptionsBuilder.cs @@ -9,22 +9,14 @@ namespace Microsoft.Extensions.DependencyInjection; public class EventMappingOptionsBuilder { - private readonly IServiceCollection _services; private readonly List _mappersToRegister = new(); private readonly List _assembliesToRegisterMappers = new(); private bool _ignoreUncoveredEvents = false; - public EventMappingOptionsBuilder(IServiceCollection services) + public EventMappingOptionsBuilder() { - _services = services; } - public EventMappingOptionsBuilder AddMappers(bool registerDefaultMappers = true) - { - _assembliesToRegisterMappers.Add(new EventMapperAssembly(Assembly.GetEntryAssembly()!, registerDefaultMappers)); - return this; - } - public EventMappingOptionsBuilder AddMappers(Assembly assembly, bool registerDefaultMappers = true) { _assembliesToRegisterMappers.Add(new EventMapperAssembly(assembly, registerDefaultMappers)); @@ -43,9 +35,10 @@ public EventMappingOptionsBuilder IgnoreUncoveredEvents() return this; } - public void Build() + public EventMappingOptions Build() { - _services.AddScoped(); + var services = new ServiceCollection(); + services.AddScoped(); var eventMappers = new List (); foreach (var assembly in _assembliesToRegisterMappers) { @@ -70,7 +63,7 @@ public void Build() } foreach (var eventMapper in eventMappers) - _services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), eventMapper.Type)); + services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), eventMapper.Type)); var alreadyCoveredEvents = eventMappers.Select(mapper => mapper.EventType).ToList(); foreach (var assembly in _assembliesToRegisterMappers.Where(a => a.RegisterDefaultMappers)) @@ -85,7 +78,7 @@ public void Build() if (alreadyCoveredEvents.Contains(eventType)) continue; var defaultEventMapperType = typeof(DefaultEventMapper<>).MakeGenericType(eventType); - _services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), defaultEventMapperType)); + services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), defaultEventMapperType)); alreadyCoveredEvents.Add(eventType); } } @@ -98,17 +91,16 @@ public void Build() .GetGenericArguments()[0]; if (alreadyCoveredEvents.Contains(eventType)) throw new InvalidOperationException($"The mapper {mapper.Name} cannot be registered by the AddMapper() method.\nThere is already an event mapper for the event type {eventType.Name}."); - _services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), mapper)); + services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventMapper), mapper)); alreadyCoveredEvents.Add(eventType); } var uncoveredEvents = _assembliesToRegisterMappers.SelectMany(assembly => assembly.Assembly.GetTypes() .Where(t => t is { IsAbstract: false, IsInterface: false } && t.GetInterfaces().Any(i => i == typeof(IEvent)) && !alreadyCoveredEvents.Contains(t))).ToList(); - foreach(var eventType in alreadyCoveredEvents) - _services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEvent), eventType)); - - if (_ignoreUncoveredEvents || !uncoveredEvents.Any()) return; + if (_ignoreUncoveredEvents || !uncoveredEvents.Any()) + return new EventMappingOptions(services, alreadyCoveredEvents, uncoveredEvents); + var uncoveredEventNames = string.Join(", ", uncoveredEvents.Select(x => x.Name)); throw new InvalidOperationException($"There are uncovered events (in mappings): {uncoveredEventNames}"); } diff --git a/src/EventSourcing/DI/EventProjectionOptions.cs b/src/EventSourcing/DI/EventProjectionOptions.cs new file mode 100644 index 0000000..333cfe6 --- /dev/null +++ b/src/EventSourcing/DI/EventProjectionOptions.cs @@ -0,0 +1,3 @@ +namespace Microsoft.Extensions.DependencyInjection; + +public record EventProjectionOptions(IServiceCollection Services, IEnumerable CoveredEvents, IEnumerable? UncoveredEvents); \ No newline at end of file diff --git a/src/EventSourcing/DI/EventProjectionOptionsBuilder.cs b/src/EventSourcing/DI/EventProjectionOptionsBuilder.cs index 8c575db..5695f88 100644 --- a/src/EventSourcing/DI/EventProjectionOptionsBuilder.cs +++ b/src/EventSourcing/DI/EventProjectionOptionsBuilder.cs @@ -7,13 +7,11 @@ namespace Microsoft.Extensions.DependencyInjection; public class EventProjectionOptionsBuilder { - private readonly IServiceCollection _services; private readonly List _assembliesToRegisterProjections = new(); private bool _ignoreUncoveredEvents; - public EventProjectionOptionsBuilder(IServiceCollection services) + public EventProjectionOptionsBuilder() { - _services = services; } public EventProjectionOptionsBuilder AddProjections() @@ -34,8 +32,9 @@ public EventProjectionOptionsBuilder IgnoreUncoveredEvents() return this; } - public void Build() + public EventProjectionOptions Build(IEnumerable? recentlyFoundEvents = null) { + var services = new ServiceCollection(); var projectionTypes = new List(); foreach (var assembly in _assembliesToRegisterProjections) { @@ -52,15 +51,15 @@ public void Build() foreach (var projectionType in projectionTypes) { var genericType = typeof(IEventHandler<>).MakeGenericType(projectionType.EventType); - _services.TryAddEnumerable(ServiceDescriptor.Transient(genericType, projectionType.Type)); - _services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventHandler), projectionType.Type)); + services.TryAddEnumerable(ServiceDescriptor.Transient(genericType, projectionType.Type)); + services.TryAddEnumerable(ServiceDescriptor.Transient(typeof(IEventHandler), projectionType.Type)); alreadyCoveredEvents.Add(projectionType.EventType); } - if (_ignoreUncoveredEvents) return; - var allEvents = _services.Where(descriptor => descriptor.ServiceType == typeof(IEvent)).Select(descriptor => descriptor.ImplementationType).ToList(); - var uncoveredEvents = allEvents.Except(alreadyCoveredEvents).ToList(); - if (!uncoveredEvents.Any()) return; + var uncoveredEvents = (recentlyFoundEvents ?? new List()).Except(alreadyCoveredEvents).ToList(); + if (_ignoreUncoveredEvents || !uncoveredEvents.Any()) + return new EventProjectionOptions(services, alreadyCoveredEvents, uncoveredEvents); + var uncoveredEventNames = string.Join(", ", uncoveredEvents.Select(x => x?.FullName)); throw new InvalidOperationException($"There are uncovered events (in projections): {uncoveredEventNames}"); } diff --git a/src/EventSourcing/DI/EventSourcingOptionsBuilder.cs b/src/EventSourcing/DI/EventSourcingOptionsBuilder.cs index 20842ec..2e61259 100644 --- a/src/EventSourcing/DI/EventSourcingOptionsBuilder.cs +++ b/src/EventSourcing/DI/EventSourcingOptionsBuilder.cs @@ -25,8 +25,8 @@ public class EventSourcingOptionsBuilder public EventSourcingOptionsBuilder(IServiceCollection services) { _services = services; - _eventMappingOptionsBuilder = new EventMappingOptionsBuilder(services); - _eventProjectionOptionsBuilder = new EventProjectionOptionsBuilder(services); + _eventMappingOptionsBuilder = new EventMappingOptionsBuilder(); + _eventProjectionOptionsBuilder = new EventProjectionOptionsBuilder(); } /// @@ -88,19 +88,27 @@ public EventSourcingOptionsBuilder Extend(Action extension) public void Build() { if (!_databaseConfigured) - _dbContextOptionsBuilderAction = options => options.UseInMemoryDatabase("EventStore"); + throw new InvalidOperationException("The event store database context must be configured."); + if (!_mappingConfigured) + throw new InvalidOperationException("The event mapping must be configured."); + if (!_projectionsConfigured) + throw new InvalidOperationException("The event projections must be configured."); + _services.AddDbContext(opt => _dbContextOptionsBuilderAction?.Invoke(opt)); _services.AddScoped(); _services.AddScoped(); _services.AddTransient(); - - if (!_mappingConfigured) - _eventMappingOptionsBuilder.AddMappers(); + if (!_projectionsConfigured) _eventProjectionOptionsBuilder.AddProjections(); - _eventMappingOptionsBuilder.Build(); - _eventProjectionOptionsBuilder.Build(); + var eventMappingOptions = _eventMappingOptionsBuilder.Build(); + foreach (var service in eventMappingOptions.Services) + _services.Add(service); + + var eventProjectionOptions = _eventProjectionOptionsBuilder.Build(eventMappingOptions.CoveredEvents); + foreach (var service in eventProjectionOptions.Services) + _services.Add(service); foreach (var extension in _extensions) extension(_services); diff --git a/src/EventSourcing/EventSourcing.csproj b/src/EventSourcing/EventSourcing.csproj index 70b3cab..bf40019 100644 --- a/src/EventSourcing/EventSourcing.csproj +++ b/src/EventSourcing/EventSourcing.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable true @@ -17,7 +17,7 @@ The persistance is based on the EntityFramworkCore and can be configured via Dep https://github.com/mrmorrandir/EventSourcing git EventSourcing, Events, EventStore - 1.0.0-beta-6 + 1.0.0-preview-20240513-0708 Breaking Changes - New Mapper Logic @@ -27,15 +27,16 @@ The persistance is based on the EntityFramworkCore and can be configured via Dep - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - + + + + + + diff --git a/tests/EventSourcing.Benchmarks/EventSourcing.Benchmarks.csproj b/tests/EventSourcing.Benchmarks/EventSourcing.Benchmarks.csproj index cbed272..50dec2c 100644 --- a/tests/EventSourcing.Benchmarks/EventSourcing.Benchmarks.csproj +++ b/tests/EventSourcing.Benchmarks/EventSourcing.Benchmarks.csproj @@ -2,15 +2,16 @@ Exe - net6.0 + net8.0 enable enable - - + + + diff --git a/tests/EventSourcing.FunctionTests.DI.InvalidAssembly/EventSourcing.FunctionTests.DI.InvalidAssembly.csproj b/tests/EventSourcing.FunctionTests.DI.InvalidAssembly/EventSourcing.FunctionTests.DI.InvalidAssembly.csproj index 0136ce4..bcd6d24 100644 --- a/tests/EventSourcing.FunctionTests.DI.InvalidAssembly/EventSourcing.FunctionTests.DI.InvalidAssembly.csproj +++ b/tests/EventSourcing.FunctionTests.DI.InvalidAssembly/EventSourcing.FunctionTests.DI.InvalidAssembly.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/tests/EventSourcing.FunctionTests.DI.InvalidAssembly2/EventSourcing.FunctionTests.DI.InvalidAssembly2.csproj b/tests/EventSourcing.FunctionTests.DI.InvalidAssembly2/EventSourcing.FunctionTests.DI.InvalidAssembly2.csproj index 8d34808..51435ff 100644 --- a/tests/EventSourcing.FunctionTests.DI.InvalidAssembly2/EventSourcing.FunctionTests.DI.InvalidAssembly2.csproj +++ b/tests/EventSourcing.FunctionTests.DI.InvalidAssembly2/EventSourcing.FunctionTests.DI.InvalidAssembly2.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 enable enable diff --git a/tests/EventSourcing.FunctionTests.DI.ValidAssembly/EventSourcing.FunctionTests.DI.ValidAssembly.csproj b/tests/EventSourcing.FunctionTests.DI.ValidAssembly/EventSourcing.FunctionTests.DI.ValidAssembly.csproj index c67df64..e16696c 100644 --- a/tests/EventSourcing.FunctionTests.DI.ValidAssembly/EventSourcing.FunctionTests.DI.ValidAssembly.csproj +++ b/tests/EventSourcing.FunctionTests.DI.ValidAssembly/EventSourcing.FunctionTests.DI.ValidAssembly.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable diff --git a/tests/EventSourcing.FunctionTests.DI/DependencyInjectionTests.cs b/tests/EventSourcing.FunctionTests.DI/DependencyInjectionTests.cs index a226eea..628328c 100644 --- a/tests/EventSourcing.FunctionTests.DI/DependencyInjectionTests.cs +++ b/tests/EventSourcing.FunctionTests.DI/DependencyInjectionTests.cs @@ -2,6 +2,7 @@ using EventSourcing.Mappers; using EventSourcing.Projections; using EventSourcing.Publishers.RabbitMQPublisher; +using EventSourcing.Repositories; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; @@ -23,6 +24,7 @@ public void RegistrationShouldWork_WhenEventsAndMappersAndProjectionsAreCorrect( var eventMappers = provider.GetRequiredService>().ToArray(); var projections = provider.GetRequiredService>().Select(s => (IEventHandler)s).ToList(); + var eventRepository = provider.GetRequiredService(); eventMappers.Should().ContainSingle(m => m.Types.Contains("my-custom-event-v1") && m.EventType == typeof(ValidAssembly.Events.CustomEvent)); eventMappers.Should().ContainSingle(m => m.Types.Contains("default-event-v1") && m.EventType == typeof(ValidAssembly.Events.DefaultEvent)); diff --git a/tests/EventSourcing.FunctionTests.DI/EventSourcing.FunctionTests.DI.csproj b/tests/EventSourcing.FunctionTests.DI/EventSourcing.FunctionTests.DI.csproj index 062e638..2998e34 100644 --- a/tests/EventSourcing.FunctionTests.DI/EventSourcing.FunctionTests.DI.csproj +++ b/tests/EventSourcing.FunctionTests.DI/EventSourcing.FunctionTests.DI.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable @@ -11,8 +11,9 @@ - - + + + diff --git a/tests/EventSourcing.FunctionTests/EventSourcing.FunctionTests.csproj b/tests/EventSourcing.FunctionTests/EventSourcing.FunctionTests.csproj index 7c5ff81..a53abc8 100644 --- a/tests/EventSourcing.FunctionTests/EventSourcing.FunctionTests.csproj +++ b/tests/EventSourcing.FunctionTests/EventSourcing.FunctionTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable @@ -10,8 +10,9 @@ - - + + + diff --git a/tests/EventSourcing.FunctionTests/Mappers/EventRegistryTests.cs b/tests/EventSourcing.FunctionTests/Mappers/EventRegistryTests.cs index c2bc2bb..eac39a8 100644 --- a/tests/EventSourcing.FunctionTests/Mappers/EventRegistryTests.cs +++ b/tests/EventSourcing.FunctionTests/Mappers/EventRegistryTests.cs @@ -2,6 +2,7 @@ using System.Text.Json; using EventSourcing.Mappers; using EventSourcing.Repositories; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json; using JsonConverter = System.Text.Json.Serialization.JsonConverter; @@ -17,6 +18,7 @@ public void DependencyInjection_ShouldRegisterAbstractEventMappersInAssembly() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); @@ -35,6 +37,7 @@ public void SerializeAndDeserialize_ShouldSucceed_ForNewestVersion() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); @@ -60,6 +63,7 @@ public void Deserialize_ShouldSucceed_ForOldVersion2() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); @@ -82,6 +86,7 @@ public void Deserialize_ShouldSucceed_ForOldVersion1() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); @@ -104,6 +109,7 @@ public void Deserialize_ShouldThrowException_WhenMapperNotFound() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); @@ -123,6 +129,7 @@ public void Serialize_ShouldThrowException_WhenMapperNotFound() var services = new ServiceCollection(); services.AddEventSourcing(config => { + config.ConfigureEventStoreDbContext(options => options.UseInMemoryDatabase("Test")); config.ConfigureMapping(options => options.AddMapper()); config.ConfigureProjections(options => options.IgnoreUncoveredEvents()); }); diff --git a/tests/EventSourcing.Publishers.RabbitMQ.IntegrationTests/EventSourcing.Publishers.RabbitMQ.IntegrationTests.csproj b/tests/EventSourcing.Publishers.RabbitMQ.IntegrationTests/EventSourcing.Publishers.RabbitMQ.IntegrationTests.csproj index 4a9ddc8..675b6a0 100644 --- a/tests/EventSourcing.Publishers.RabbitMQ.IntegrationTests/EventSourcing.Publishers.RabbitMQ.IntegrationTests.csproj +++ b/tests/EventSourcing.Publishers.RabbitMQ.IntegrationTests/EventSourcing.Publishers.RabbitMQ.IntegrationTests.csproj @@ -1,7 +1,7 @@ - net6.0 + net8.0 enable enable @@ -11,8 +11,9 @@ - - + + +