From 387eac83dfc04215ed9e99bbd85cdafdef9a8b25 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 5 Aug 2025 14:37:52 -0500 Subject: [PATCH 1/4] [WriteAggregate] and [Aggregate] are required by default --- src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs b/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs index 7bafba5f0..c08a0ffcb 100644 --- a/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs +++ b/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs @@ -29,7 +29,7 @@ public WriteAggregateAttribute(string? routeOrParameterName) public string? RouteOrParameterName { get; } - public bool Required { get; set; } + public bool Required { get; set; } = true; public string MissingMessage { get; set; } public OnMissing OnMissing { get; set; } From ef7a4a47938e788ff151405f715fd648e18a2238 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 5 Aug 2025 16:28:36 -0500 Subject: [PATCH 2/4] Addressed a couple problems w/ [Document] not soft deleted, and fixed a failing test --- docs/guide/durability/marten/event-sourcing.md | 7 ++++++- .../Marten/reacting_to_read_aggregate.cs | 5 +++-- src/Persistence/Wolverine.Marten/AggregateHandling.cs | 10 +++++----- .../Persistence/Sagas/LoadDocumentFrame.cs | 1 + .../Sagas/MartenPersistenceFrameProvider.cs | 1 + .../Wolverine.Marten/WriteAggregateAttribute.cs | 2 +- src/Wolverine/Persistence/EntityAttribute.cs | 7 +++++-- 7 files changed, 22 insertions(+), 11 deletions(-) diff --git a/docs/guide/durability/marten/event-sourcing.md b/docs/guide/durability/marten/event-sourcing.md index 05cb44c2a..a640fc1d7 100644 --- a/docs/guide/durability/marten/event-sourcing.md +++ b/docs/guide/durability/marten/event-sourcing.md @@ -228,7 +228,12 @@ public class MarkItemReadyHandler1442193977 : MessageHandler As you probably guessed, there are some naming conventions or other questions you need to be aware of before you use this middleware strategy. -Alternatively, there is also the newer `[WriteAttribute]` usage, with this example being a functional alternative +::: info +The `[Aggregate]` and `[WriteAggregate]` attributes are _required by default_, meaning that the handler or HTTP +endpoint will be stopped if the data is not found. You can explicitly mark individual attributes as `Required=false`. +::: + +Alternatively, there is also the newer `[WriteAggregate]` usage, with this example being a functional alternative mark up: diff --git a/src/Http/Wolverine.Http.Tests/Marten/reacting_to_read_aggregate.cs b/src/Http/Wolverine.Http.Tests/Marten/reacting_to_read_aggregate.cs index 5414c5efa..118ae7db0 100644 --- a/src/Http/Wolverine.Http.Tests/Marten/reacting_to_read_aggregate.cs +++ b/src/Http/Wolverine.Http.Tests/Marten/reacting_to_read_aggregate.cs @@ -123,7 +123,7 @@ await theHost.Scenario(x => public static class LetterAggregateEndpointWithValidation { - public static ProblemDetails Validate([WriteAggregate(Required = true, OnMissing = OnMissing.ProblemDetailsWith404)] LetterAggregate letters) + public static ProblemDetails Validate(LetterAggregate letters) { if (letters.ACount is 0) { @@ -134,10 +134,11 @@ public static ProblemDetails Validate([WriteAggregate(Required = true, OnMissing } [WolverinePost("/letters-validation/{id}")] - public static LetterAggregate PostLetter(LetterAggregate letters) + public static LetterAggregate PostLetter([WriteAggregate(Required = true, OnMissing = OnMissing.ProblemDetailsWith404)] LetterAggregate letters) => letters; } + public static class LetterAggregateEndpoint { #region sample_read_aggregate_fine_grained_validation_control diff --git a/src/Persistence/Wolverine.Marten/AggregateHandling.cs b/src/Persistence/Wolverine.Marten/AggregateHandling.cs index 06df0280d..f513d7c70 100644 --- a/src/Persistence/Wolverine.Marten/AggregateHandling.cs +++ b/src/Persistence/Wolverine.Marten/AggregateHandling.cs @@ -183,11 +183,6 @@ internal static void DetermineEventCaptureHandling(IChain chain, MethodCall firs internal Variable RelayAggregateToHandlerMethod(Variable eventStream, IChain chain, MethodCall firstCall, Type aggregateType) { - if (aggregateType.Name == "LetterAggregate") - { - Debug.WriteLine("Here"); - } - Variable aggregateVariable = new MemberAccessVariable(eventStream, typeof(IEventStream<>).MakeGenericType(aggregateType).GetProperty(nameof(IEventStream.Aggregate))); @@ -211,6 +206,11 @@ internal Variable RelayAggregateToHandlerMethod(Variable eventStream, IChain cha firstCall.TrySetArgument(aggregateVariable); } + foreach (var methodCall in chain.Middleware.OfType()) + { + methodCall.TrySetArgument(aggregateVariable); + } + return aggregateVariable; } diff --git a/src/Persistence/Wolverine.Marten/Persistence/Sagas/LoadDocumentFrame.cs b/src/Persistence/Wolverine.Marten/Persistence/Sagas/LoadDocumentFrame.cs index e5e3a78e1..ea829ba85 100644 --- a/src/Persistence/Wolverine.Marten/Persistence/Sagas/LoadDocumentFrame.cs +++ b/src/Persistence/Wolverine.Marten/Persistence/Sagas/LoadDocumentFrame.cs @@ -18,6 +18,7 @@ internal class LoadDocumentFrame : AsyncFrame public LoadDocumentFrame(Type sagaType, Variable sagaId) { _sagaId = sagaId; + uses.Add(sagaId); Saga = new Variable(sagaType, this); } diff --git a/src/Persistence/Wolverine.Marten/Persistence/Sagas/MartenPersistenceFrameProvider.cs b/src/Persistence/Wolverine.Marten/Persistence/Sagas/MartenPersistenceFrameProvider.cs index 770c266da..8a19b5c6f 100644 --- a/src/Persistence/Wolverine.Marten/Persistence/Sagas/MartenPersistenceFrameProvider.cs +++ b/src/Persistence/Wolverine.Marten/Persistence/Sagas/MartenPersistenceFrameProvider.cs @@ -137,6 +137,7 @@ internal class SetVariableToNullIfSoftDeletedFrame : AsyncFrame public SetVariableToNullIfSoftDeletedFrame(Variable entity) { _entity = entity; + uses.Add(entity); } public override void GenerateCode(GeneratedMethod method, ISourceWriter writer) diff --git a/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs b/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs index c08a0ffcb..dc877958f 100644 --- a/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs +++ b/src/Persistence/Wolverine.Marten/WriteAggregateAttribute.cs @@ -45,7 +45,7 @@ public override Variable Modify(IChain chain, ParameterInfo parameter, IServiceC if (chain.HandlerCalls().First().Method.GetParameters().Count(x => x.HasAttribute()) > 1) { throw new InvalidOperationException( - "It is only possible (today) to use a single [Aggregate] attribute on an HTTP handler method. Maybe use [ReadAggregate] if all you need is the projected data"); + "It is only possible (today) to use a single [Aggregate] or [WriteAggregate] attribute on an HTTP handler method. Maybe use [ReadAggregate] if all you need is the projected data"); } var aggregateType = parameter.ParameterType; diff --git a/src/Wolverine/Persistence/EntityAttribute.cs b/src/Wolverine/Persistence/EntityAttribute.cs index f5b717b69..b404cac38 100644 --- a/src/Wolverine/Persistence/EntityAttribute.cs +++ b/src/Wolverine/Persistence/EntityAttribute.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Reflection; using JasperFx.CodeGeneration; using JasperFx.CodeGeneration.Frames; @@ -42,6 +43,10 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer) _guardFrames[0].GenerateCode(method, writer); } + else if (_creator.Next != null) + { + Debug.WriteLine("What the heck?"); + } else { var previous = _creator; @@ -87,8 +92,6 @@ public EntityAttribute(string argumentName) : base(argumentName) { ValueSource = ValueSource.Anything; } - - /// /// Is the existence of this entity required for the rest of the handler action or HTTP endpoint From 9e40b735f1494810eed80b29687857a29157568d Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 5 Aug 2025 16:34:05 -0500 Subject: [PATCH 3/4] Warning in docs for using the [Aggregate] atts on "Before" methods. Closes GH-1622 --- docs/guide/durability/marten/event-sourcing.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/guide/durability/marten/event-sourcing.md b/docs/guide/durability/marten/event-sourcing.md index a640fc1d7..1e207fd4d 100644 --- a/docs/guide/durability/marten/event-sourcing.md +++ b/docs/guide/durability/marten/event-sourcing.md @@ -228,9 +228,14 @@ public class MarkItemReadyHandler1442193977 : MessageHandler As you probably guessed, there are some naming conventions or other questions you need to be aware of before you use this middleware strategy. +::: warning +There are some open, let's call them _imperfections_ with Wolverine's code generation against the `[WriteAggregate]` and `[ReadAggregate]` +usage. For best results, only use these attributes on a parameter within the main HTTP endpoint method and not in `Validate/Before/Load` methods. +::: + ::: info -The `[Aggregate]` and `[WriteAggregate]` attributes are _required by default_, meaning that the handler or HTTP -endpoint will be stopped if the data is not found. You can explicitly mark individual attributes as `Required=false`. +The `[Aggregate]` and `[WriteAggregate]` attributes _require the requested stream and aggregate to be found by default_, meaning that the handler or HTTP +endpoint will be stopped if the requested data is not found. You can explicitly mark individual attributes as `Required=false`. ::: Alternatively, there is also the newer `[WriteAggregate]` usage, with this example being a functional alternative From be3bf2594626692b6adc49c21a271964eac3ace8 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Tue, 5 Aug 2025 16:55:48 -0500 Subject: [PATCH 4/4] updated the F# core lib across the board to make Nuget stop crying in CI --- WolverineWebApiFSharp/WolverineWebApiFSharp.fsproj | 4 ++++ src/Http/Wolverine.Http/Wolverine.Http.csproj | 2 +- src/Http/WolverineWebApi/WolverineWebApi.csproj | 2 +- .../IncidentService.Tests/IncidentService.Tests.csproj | 2 +- .../AppWithMiddleware.Tests/AppWithMiddleware.Tests.csproj | 2 +- .../MultiTenantedTodoWebService.Tests.csproj | 2 +- .../BankingService.Tests/BankingService.Tests.csproj | 2 +- .../TodoWebServiceTests/TodoWebServiceTests.csproj | 2 +- .../Kafka/Wolverine.Kafka.Tests/Wolverine.Kafka.Tests.csproj | 4 ++-- 9 files changed, 13 insertions(+), 9 deletions(-) diff --git a/WolverineWebApiFSharp/WolverineWebApiFSharp.fsproj b/WolverineWebApiFSharp/WolverineWebApiFSharp.fsproj index 0258cc599..48b4ccda3 100644 --- a/WolverineWebApiFSharp/WolverineWebApiFSharp.fsproj +++ b/WolverineWebApiFSharp/WolverineWebApiFSharp.fsproj @@ -14,4 +14,8 @@ + + + + diff --git a/src/Http/Wolverine.Http/Wolverine.Http.csproj b/src/Http/Wolverine.Http/Wolverine.Http.csproj index ff0d83710..13aa6ded0 100644 --- a/src/Http/Wolverine.Http/Wolverine.Http.csproj +++ b/src/Http/Wolverine.Http/Wolverine.Http.csproj @@ -11,7 +11,7 @@ - + diff --git a/src/Http/WolverineWebApi/WolverineWebApi.csproj b/src/Http/WolverineWebApi/WolverineWebApi.csproj index 0676e14d3..a7ad3a7a1 100644 --- a/src/Http/WolverineWebApi/WolverineWebApi.csproj +++ b/src/Http/WolverineWebApi/WolverineWebApi.csproj @@ -3,7 +3,7 @@ net9.0 - + diff --git a/src/Samples/IncidentService/IncidentService.Tests/IncidentService.Tests.csproj b/src/Samples/IncidentService/IncidentService.Tests/IncidentService.Tests.csproj index e2baad1e9..912944ddd 100644 --- a/src/Samples/IncidentService/IncidentService.Tests/IncidentService.Tests.csproj +++ b/src/Samples/IncidentService/IncidentService.Tests/IncidentService.Tests.csproj @@ -6,7 +6,7 @@ - + diff --git a/src/Samples/Middleware/AppWithMiddleware.Tests/AppWithMiddleware.Tests.csproj b/src/Samples/Middleware/AppWithMiddleware.Tests/AppWithMiddleware.Tests.csproj index 5f3a17aec..7da73b0ea 100644 --- a/src/Samples/Middleware/AppWithMiddleware.Tests/AppWithMiddleware.Tests.csproj +++ b/src/Samples/Middleware/AppWithMiddleware.Tests/AppWithMiddleware.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Samples/MultiTenantedTodoService/MultiTenantedTodoWebService.Tests/MultiTenantedTodoWebService.Tests.csproj b/src/Samples/MultiTenantedTodoService/MultiTenantedTodoWebService.Tests/MultiTenantedTodoWebService.Tests.csproj index ce97910b5..b367db52f 100644 --- a/src/Samples/MultiTenantedTodoService/MultiTenantedTodoWebService.Tests/MultiTenantedTodoWebService.Tests.csproj +++ b/src/Samples/MultiTenantedTodoService/MultiTenantedTodoWebService.Tests/MultiTenantedTodoWebService.Tests.csproj @@ -17,7 +17,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/src/Samples/TestHarness/BankingService.Tests/BankingService.Tests.csproj b/src/Samples/TestHarness/BankingService.Tests/BankingService.Tests.csproj index e3b816932..f146db892 100644 --- a/src/Samples/TestHarness/BankingService.Tests/BankingService.Tests.csproj +++ b/src/Samples/TestHarness/BankingService.Tests/BankingService.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Samples/TodoWebService/TodoWebServiceTests/TodoWebServiceTests.csproj b/src/Samples/TodoWebService/TodoWebServiceTests/TodoWebServiceTests.csproj index 3c5355412..f9ac4e1bb 100644 --- a/src/Samples/TodoWebService/TodoWebServiceTests/TodoWebServiceTests.csproj +++ b/src/Samples/TodoWebService/TodoWebServiceTests/TodoWebServiceTests.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/Transports/Kafka/Wolverine.Kafka.Tests/Wolverine.Kafka.Tests.csproj b/src/Transports/Kafka/Wolverine.Kafka.Tests/Wolverine.Kafka.Tests.csproj index 59167a285..0e2894233 100644 --- a/src/Transports/Kafka/Wolverine.Kafka.Tests/Wolverine.Kafka.Tests.csproj +++ b/src/Transports/Kafka/Wolverine.Kafka.Tests/Wolverine.Kafka.Tests.csproj @@ -18,7 +18,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + @@ -35,7 +35,7 @@ - +