From 8e1ec2abd2249f8db3ac0d5d631ba41cc52b011f Mon Sep 17 00:00:00 2001 From: Ben Edwards Date: Mon, 25 Mar 2024 14:19:55 +1100 Subject: [PATCH 1/2] Ensure that applying events to an aggregate returns the aggregate even if there are no events to apply. --- .../GeneratedAggregateProjectionBase.CodeGen.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.CodeGen.cs b/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.CodeGen.cs index 51639ba48f..c7bd6a29c3 100644 --- a/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.CodeGen.cs +++ b/src/Marten/Events/Aggregation/GeneratedAggregateProjectionBase.CodeGen.cs @@ -280,14 +280,11 @@ private void buildLiveAggregationType(GeneratedAssembly assembly) buildMethod.DerivedVariables.Add(Variable.For("(IQuerySession)session")); - buildMethod.Frames.Code("if (!events.Any()) return null;"); - - buildMethod.Frames.Add(new DeclareAggregateFrame(typeof(T))); - - var callCreateAggregateFrame = new CallCreateAggregateFrame(_createMethods); - // This is the existing snapshot passed into the LiveAggregator var snapshot = buildMethod.Arguments.Single(x => x.VariableType == typeof(T)); + buildMethod.Frames.Code($"if (!events.Any()) return {snapshot.Usage};"); + + var callCreateAggregateFrame = new CallCreateAggregateFrame(_createMethods); callCreateAggregateFrame.CoalesceAssignTo(snapshot); buildMethod.Frames.Add(callCreateAggregateFrame); From e4b0172b77e15b409cb172cbbadc7e51badb6334 Mon Sep 17 00:00:00 2001 From: Ben Edwards Date: Mon, 25 Mar 2024 14:21:27 +1100 Subject: [PATCH 2/2] VERY inconsistent bug tests --- ...itional_events_should_return_projection.cs | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs diff --git a/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs b/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs new file mode 100644 index 0000000000..2c0f854b4e --- /dev/null +++ b/src/EventSourcingTests/Bugs/Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Marten.Events; +using Marten.Events.Projections; +using Marten.Schema; +using Marten.Testing.Harness; +using Xunit; + +namespace EventSourcingTests.Bugs; + +public class Bug_9999_fetch_for_writing_async_projection_with_no_additional_events_should_return_projection : BugIntegrationContext +{ + // ! IMPORTANT - this does not fail on the bug - for some reason the dynamic codegen doesn't produce the error. + // TODO: Rework this to properly highlight the error + [Fact] + public async Task override_the_optimistic_concurrency_on_projected_document() + { + StoreOptions(opts => + { + opts.Events.StreamIdentity = StreamIdentity.AsString; + opts.Projections.Snapshot(SnapshotLifecycle.Async); + }); + + var streamKey = Guid.NewGuid().ToString(); + + theSession.Events.StartStream(streamKey, new EventA("foo"), new EventB()); + await theSession.SaveChangesAsync(); + + using var daemon = await theStore.BuildProjectionDaemonAsync(); + await daemon.RebuildProjectionAsync(CancellationToken.None); + + var result = await theSession.Events.FetchForWriting(streamKey); + + Assert.Equal(2, result.CurrentVersion); + Assert.Equal(2, result.StartingVersion); + Assert.NotNull(result.Aggregate); + Assert.Equal(streamKey, result.Aggregate.StreamKey); + // TODO: There is a weird bug here where ~25% of the time this is set to null. Seems to happen intermittently across all frameworks. No idea why. + Assert.Equal("foo", result.Aggregate.Name); + } + + public record EventA(string Name); + public record EventB; +} + +// put it here to avoid a bug with rebuilding projections whose types are nested classes +// TODO: Write up this bug +public record TestProjection +{ + [Identity] + public string StreamKey { get; set; } = null!; + + public string Name { get; set; } = null!; + + public static TestProjection Create(EventA @event) => + new TestProjection + { + Name = @event.Name, + }; +}