From 043867c5dfd26ecdc211023d6b7e66e8e40cd171 Mon Sep 17 00:00:00 2001 From: Rasmus Mikkelsen Date: Fri, 11 Nov 2022 14:31:33 +0100 Subject: [PATCH] v1: Entity Framework migration --- EventFlow.sln | 10 ++ .../EntityFrameworkConfiguration.cs | 18 +-- .../EventFlow.EntityFramework.csproj | 16 ++- .../EntityFrameworkEventPersistence.cs | 113 ++++++++---------- .../IEntityFrameworkConfiguration.cs | 4 +- 5 files changed, 85 insertions(+), 76 deletions(-) diff --git a/EventFlow.sln b/EventFlow.sln index 32cdee44f..2e60dc295 100644 --- a/EventFlow.sln +++ b/EventFlow.sln @@ -41,6 +41,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventFlow.MongoDB", "Source EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventFlow.MongoDB.Tests", "Source\EventFlow.MongoDB.Tests\EventFlow.MongoDB.Tests.csproj", "{9132960E-9496-4C77-BD80-A09A86814757}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EntityFramework", "EntityFramework", "{2EAEC5A8-CE58-47F6-AB61-F78FECE714D5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventFlow.EntityFramework", "Source\EventFlow.EntityFramework\EventFlow.EntityFramework.csproj", "{5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -99,6 +103,10 @@ Global {9132960E-9496-4C77-BD80-A09A86814757}.Debug|Any CPU.Build.0 = Debug|Any CPU {9132960E-9496-4C77-BD80-A09A86814757}.Release|Any CPU.ActiveCfg = Release|Any CPU {9132960E-9496-4C77-BD80-A09A86814757}.Release|Any CPU.Build.0 = Release|Any CPU + {5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -118,6 +126,8 @@ Global {9B07B6E9-428A-42F7-AFB1-B23755A409EE} = {5EE323DE-E69B-451A-8AC3-22DD6A004FBA} {BF9A0D20-4F8E-443C-8F36-5DD854D188C2} = {9B07B6E9-428A-42F7-AFB1-B23755A409EE} {9132960E-9496-4C77-BD80-A09A86814757} = {9B07B6E9-428A-42F7-AFB1-B23755A409EE} + {2EAEC5A8-CE58-47F6-AB61-F78FECE714D5} = {5EE323DE-E69B-451A-8AC3-22DD6A004FBA} + {5B2CBCD6-C8E1-44B9-A258-AE2A252B00E3} = {2EAEC5A8-CE58-47F6-AB61-F78FECE714D5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17607E2C-4E8E-45A2-85BD-0A5808E1C0F3} diff --git a/Source/EventFlow.EntityFramework/EntityFrameworkConfiguration.cs b/Source/EventFlow.EntityFramework/EntityFrameworkConfiguration.cs index a554aba7e..47894477c 100644 --- a/Source/EventFlow.EntityFramework/EntityFrameworkConfiguration.cs +++ b/Source/EventFlow.EntityFramework/EntityFrameworkConfiguration.cs @@ -22,14 +22,14 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; -using EventFlow.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace EventFlow.EntityFramework { public class EntityFrameworkConfiguration : IEntityFrameworkConfiguration { - private Action _registerUniqueConstraintDetectionStrategy; - private Action _registerBulkOperationConfiguration; + private Action _registerUniqueConstraintDetectionStrategy; + private Action _registerBulkOperationConfiguration; public static EntityFrameworkConfiguration New => new EntityFrameworkConfiguration(); @@ -39,24 +39,24 @@ private EntityFrameworkConfiguration() UseBulkOperationConfiguration(); } - void IEntityFrameworkConfiguration.Apply(IServiceRegistration serviceRegistration) + void IEntityFrameworkConfiguration.Apply(IServiceCollection serviceCollection) { - serviceRegistration.Register(s => this); - _registerUniqueConstraintDetectionStrategy(serviceRegistration); - _registerBulkOperationConfiguration(serviceRegistration); + serviceCollection.AddTransient(s => this); + _registerUniqueConstraintDetectionStrategy(serviceCollection); + _registerBulkOperationConfiguration(serviceCollection); } public EntityFrameworkConfiguration UseBulkOperationConfiguration() where T : class, IBulkOperationConfiguration { - _registerBulkOperationConfiguration = s => s.Register(); + _registerBulkOperationConfiguration = s => s.AddTransient(); return this; } public EntityFrameworkConfiguration UseUniqueConstraintDetectionStrategy() where T : class, IUniqueConstraintDetectionStrategy { - _registerUniqueConstraintDetectionStrategy = s => s.Register(); + _registerUniqueConstraintDetectionStrategy = s => s.AddTransient(); return this; } } diff --git a/Source/EventFlow.EntityFramework/EventFlow.EntityFramework.csproj b/Source/EventFlow.EntityFramework/EventFlow.EntityFramework.csproj index 4a67c176c..2ee66ca29 100644 --- a/Source/EventFlow.EntityFramework/EventFlow.EntityFramework.csproj +++ b/Source/EventFlow.EntityFramework/EventFlow.EntityFramework.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netcoreapp3.1 + netstandard2.1;netcoreapp3.1;net6.0 True True False @@ -9,7 +9,7 @@ EventFlow.EntityFramework Frank Ebersoll Rasmus Mikkelsen - Copyright (c) Rasmus Mikkelsen 2015 - 2021 + Copyright (c) Rasmus Mikkelsen 2015 - 2022 Entity Framework Core support for EventFlow CQRS ES event sourcing EF Entity Framework Core git @@ -20,15 +20,21 @@ UPDATED BY BUILD - + - 3.1.5 + 3.1.31 - 3.1.1 + 3.1.31 + + + + + + 6.0.11 diff --git a/Source/EventFlow.EntityFramework/EventStores/EntityFrameworkEventPersistence.cs b/Source/EventFlow.EntityFramework/EventStores/EntityFrameworkEventPersistence.cs index 714fd8e4e..44a6e39a2 100644 --- a/Source/EventFlow.EntityFramework/EventStores/EntityFrameworkEventPersistence.cs +++ b/Source/EventFlow.EntityFramework/EventStores/EntityFrameworkEventPersistence.cs @@ -31,25 +31,24 @@ using EventFlow.EntityFramework.Extensions; using EventFlow.EventStores; using EventFlow.Exceptions; -using EventFlow.Logs; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; namespace EventFlow.EntityFramework.EventStores { public class EntityFrameworkEventPersistence : IEventPersistence where TDbContext : DbContext { + private readonly ILogger> _logger; private readonly IDbContextProvider _contextProvider; - private readonly ILog _log; private readonly IUniqueConstraintDetectionStrategy _strategy; public EntityFrameworkEventPersistence( - ILog log, + ILogger> logger, IDbContextProvider contextProvider, - IUniqueConstraintDetectionStrategy strategy - ) + IUniqueConstraintDetectionStrategy strategy) { - _log = log; + _logger = logger; _contextProvider = contextProvider; _strategy = strategy; } @@ -61,29 +60,29 @@ public async Task LoadAllCommittedEvents(GlobalPosition ? 0 : long.Parse(globalPosition.Value); - using (var context = _contextProvider.CreateContext()) - { - var entities = await context - .Set() - .OrderBy(e => e.GlobalSequenceNumber) - .Where(e => e.GlobalSequenceNumber >= startPosition) - .Take(pageSize) - .ToListAsync(cancellationToken) - .ConfigureAwait(false); - - var nextPosition = entities.Any() - ? entities.Max(e => e.GlobalSequenceNumber) + 1 - : startPosition; - - return new AllCommittedEventsPage(new GlobalPosition(nextPosition.ToString()), entities); - } + await using var context = _contextProvider.CreateContext(); + + var entities = await context + .Set() + .OrderBy(e => e.GlobalSequenceNumber) + .Where(e => e.GlobalSequenceNumber >= startPosition) + .Take(pageSize) + .ToListAsync(cancellationToken); + + var nextPosition = entities.Any() + ? entities.Max(e => e.GlobalSequenceNumber) + 1 + : startPosition; + + return new AllCommittedEventsPage(new GlobalPosition(nextPosition.ToString()), entities); } public async Task> CommitEventsAsync(IIdentity id, IReadOnlyCollection serializedEvents, CancellationToken cancellationToken) { if (!serializedEvents.Any()) - return new ICommittedDomainEvent[0]; + { + return Array.Empty(); + } var entities = serializedEvents .Select((e, i) => new EventEntity @@ -97,24 +96,22 @@ public async Task> CommitEventsAsync( }) .ToList(); - _log.Verbose( - "Committing {0} events to EntityFramework event store for entity with ID '{1}'", + _logger.LogTrace( + "Committing {Count} events to EntityFramework event store for entity with ID '{Id}'", entities.Count, id); try { - using (var context = _contextProvider.CreateContext()) - { - await context.AddRangeAsync(entities, cancellationToken); - await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - } + await using var context = _contextProvider.CreateContext(); + await context.AddRangeAsync(entities, cancellationToken); + await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); } catch (DbUpdateException ex) when (ex.IsUniqueConstraintViolation(_strategy)) { - _log.Verbose( + _logger.LogTrace( "Entity Framework event insert detected an optimistic concurrency " + - "exception for entity with ID '{0}'", id); + "exception for entity with ID '{Id}'", id); throw new OptimisticConcurrencyException(ex.Message, ex); } @@ -124,38 +121,34 @@ public async Task> CommitEventsAsync( public async Task> LoadCommittedEventsAsync(IIdentity id, int fromEventSequenceNumber, CancellationToken cancellationToken) { - using (var context = _contextProvider.CreateContext()) - { - var entities = await context - .Set() - .Where(e => e.AggregateId == id.Value - && e.AggregateSequenceNumber >= fromEventSequenceNumber) - .OrderBy(e => e.AggregateSequenceNumber) - .ToListAsync(cancellationToken) - .ConfigureAwait(false); - - return entities; - } + await using var context = _contextProvider.CreateContext(); + + var entities = await context + .Set() + .Where(e => e.AggregateId == id.Value + && e.AggregateSequenceNumber >= fromEventSequenceNumber) + .OrderBy(e => e.AggregateSequenceNumber) + .ToListAsync(cancellationToken); + + return entities; } public async Task DeleteEventsAsync(IIdentity id, CancellationToken cancellationToken) { - using (var context = _contextProvider.CreateContext()) - { - var entities = await context.Set() - .Where(e => e.AggregateId == id.Value) - .Select(e => new EventEntity {GlobalSequenceNumber = e.GlobalSequenceNumber}) - .ToListAsync(cancellationToken) - .ConfigureAwait(false); - - context.RemoveRange(entities); - var rowsAffected = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); - - _log.Verbose( - "Deleted entity with ID '{0}' by deleting all of its {1} events", - id, - rowsAffected); - } + await using var context = _contextProvider.CreateContext(); + + var entities = await context.Set() + .Where(e => e.AggregateId == id.Value) + .Select(e => new EventEntity {GlobalSequenceNumber = e.GlobalSequenceNumber}) + .ToListAsync(cancellationToken); + + context.RemoveRange(entities); + var rowsAffected = await context.SaveChangesAsync(cancellationToken).ConfigureAwait(false); + + _logger.LogTrace( + "Deleted entity with ID {Id} by deleting all of its {NumberOfEvents} events", + id, + rowsAffected); } } } \ No newline at end of file diff --git a/Source/EventFlow.EntityFramework/IEntityFrameworkConfiguration.cs b/Source/EventFlow.EntityFramework/IEntityFrameworkConfiguration.cs index ee3c5a77e..0e7f5c8ec 100644 --- a/Source/EventFlow.EntityFramework/IEntityFrameworkConfiguration.cs +++ b/Source/EventFlow.EntityFramework/IEntityFrameworkConfiguration.cs @@ -21,12 +21,12 @@ // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -using EventFlow.Configuration; +using Microsoft.Extensions.DependencyInjection; namespace EventFlow.EntityFramework { public interface IEntityFrameworkConfiguration { - void Apply(IServiceRegistration serviceRegistration); + void Apply(IServiceCollection serviceCollection); } } \ No newline at end of file