From 2c70b6cf38dfd741f9586eda157172523cdd71a2 Mon Sep 17 00:00:00 2001 From: Chris Philips Date: Wed, 6 Apr 2022 14:19:52 -0700 Subject: [PATCH] feat/refactor: move the boxing up a level for annotated commands this allows pattern matching on IEntityAnnotation<> where the actual command type can be used as the generic parameter. before you would have to check IEntityAnnotation<...>.Data is ; now you can check IEntityAnnotation --- .../Annotations/EntityAnnotation.cs | 28 +++++++++++++------ ...ProjectionSnapshotTransactionSubscriber.cs | 9 +++++- .../Extensions/DocumentQueryExtensions.cs | 6 ++-- .../Implementations/Entities/TestEntity.cs | 19 +++++-------- .../Projections/OneToOneProjection.cs | 21 +++++--------- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/EntityDb.Common/Annotations/EntityAnnotation.cs b/src/EntityDb.Common/Annotations/EntityAnnotation.cs index 173e1e4c..907aecc4 100644 --- a/src/EntityDb.Common/Annotations/EntityAnnotation.cs +++ b/src/EntityDb.Common/Annotations/EntityAnnotation.cs @@ -2,6 +2,7 @@ using EntityDb.Abstractions.Transactions; using EntityDb.Abstractions.Transactions.Steps; using EntityDb.Abstractions.ValueObjects; +using System; namespace EntityDb.Common.Annotations; @@ -14,16 +15,25 @@ internal record EntityAnnotation TData Data ) : IEntityAnnotation { - public static EntityAnnotation CreateFrom(ITransaction transaction, ITransactionStep transactionStep, - TData data) + public static IEntityAnnotation CreateFromBoxedData + ( + Id transactionId, + TimeStamp transactionTimeStamp, + Id entityId, + VersionNumber entityVersionNumber, + object boxedData + ) { - return new + var dataAnnotationType = typeof(EntityAnnotation<>).MakeGenericType(boxedData.GetType()); + + return (IEntityAnnotation)Activator.CreateInstance ( - transaction.Id, - transaction.TimeStamp, - transactionStep.EntityId, - transactionStep.EntityVersionNumber, - data - ); + dataAnnotationType, + transactionId, + transactionTimeStamp, + entityId, + entityVersionNumber, + boxedData + )!; } } diff --git a/src/EntityDb.Common/Transactions/ProjectionSnapshotTransactionSubscriber.cs b/src/EntityDb.Common/Transactions/ProjectionSnapshotTransactionSubscriber.cs index 2d1c01ec..d063ddfc 100644 --- a/src/EntityDb.Common/Transactions/ProjectionSnapshotTransactionSubscriber.cs +++ b/src/EntityDb.Common/Transactions/ProjectionSnapshotTransactionSubscriber.cs @@ -62,7 +62,14 @@ protected override async Task NotifyAsync(ITransaction transaction) var projection = previousProjection; - var annotatedCommand = EntityAnnotation.CreateFrom(transaction, appendCommandTransactionStep, appendCommandTransactionStep.Command); + var annotatedCommand = EntityAnnotation.CreateFromBoxedData + ( + transaction.Id, + transaction.TimeStamp, + appendCommandTransactionStep.EntityId, + appendCommandTransactionStep.EntityVersionNumber, + appendCommandTransactionStep.Command + ); projection = projection.Reduce(annotatedCommand); diff --git a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs b/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs index d082724e..b9344b89 100644 --- a/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs +++ b/src/EntityDb.MongoDb/Extensions/DocumentQueryExtensions.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -104,11 +105,12 @@ public static async Task[]> GetEntityAnnotation new EntityAnnotation + .Select(document => EntityAnnotation.CreateFromBoxedData ( document.TransactionId, document.TransactionTimeStamp, @@ -116,7 +118,7 @@ CancellationToken cancellationToken document.EntityVersionNumber, envelopeService.Reconstruct(document.Data) )) - .ToArray>(); + .ToArray(); } public static async Task GetData diff --git a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs index 68edbb72..9b19a25c 100644 --- a/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs +++ b/test/EntityDb.Common.Tests/Implementations/Entities/TestEntity.cs @@ -1,9 +1,11 @@ using EntityDb.Common.Entities; using System; +using System.Linq; using System.Threading; using EntityDb.Abstractions.Reducers; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Snapshots; +using EntityDb.Common.Tests.Implementations.Commands; using EntityDb.Common.Tests.Implementations.Snapshots; namespace EntityDb.Common.Tests.Implementations.Entities; @@ -40,19 +42,12 @@ public VersionNumber GetVersionNumber() public TestEntity Reduce(object[] commands) { - var newEntity = this; - - foreach (var command in commands) + return commands.Aggregate(this, (previousEntity, nextCommand) => nextCommand switch { - if (command is not IReducer reducer) - { - throw new NotImplementedException(); - } - - newEntity = reducer.Reduce(newEntity); - } - - return newEntity; + DoNothing doNothing => doNothing.Reduce(previousEntity), + Count count => count.Reduce(previousEntity), + _ => throw new NotSupportedException() + }); } public static AsyncLocal?> ShouldReplaceLogic { get; } = new(); diff --git a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs index a41c386b..335a9682 100644 --- a/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs +++ b/test/EntityDb.Common.Tests/Implementations/Projections/OneToOneProjection.cs @@ -1,10 +1,12 @@ using System; +using System.Linq; using System.Threading; using EntityDb.Abstractions.Annotations; using EntityDb.Abstractions.Reducers; using EntityDb.Abstractions.ValueObjects; using EntityDb.Common.Projections; using EntityDb.Common.Snapshots; +using EntityDb.Common.Tests.Implementations.Commands; using EntityDb.Common.Tests.Implementations.Snapshots; namespace EntityDb.Common.Tests.Implementations.Projections; @@ -34,21 +36,12 @@ public VersionNumber GetEntityVersionNumber(Id entityId) public OneToOneProjection Reduce(params IEntityAnnotation[] annotatedCommands) { - var newProjection = this; - - foreach (var annotatedCommand in annotatedCommands) + return annotatedCommands.Aggregate(this, (previousProjection, nextAnnotatedCommand) => nextAnnotatedCommand switch { - var command = annotatedCommand.Data; - - if (command is not IReducer reducer) - { - throw new NotImplementedException(); - } - - newProjection = reducer.Reduce(newProjection); - } - - return newProjection; + IEntityAnnotation doNothing => doNothing.Data.Reduce(previousProjection), + IEntityAnnotation count => count.Data.Reduce(previousProjection), + _ => throw new NotSupportedException() + }); } public static AsyncLocal?> ShouldReplaceLogic { get; } = new();