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, + }; +} 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);