From 4b109930dd6ab53250c0a221e8dd539e0662e01e Mon Sep 17 00:00:00 2001 From: hsnabszhdn <16090309+hsnabszhdn@users.noreply.github.com> Date: Sun, 16 Mar 2025 08:32:31 +1100 Subject: [PATCH] docs: commit 'mdsnippets' changes --- docs/guide/basics.md | 8 +-- docs/guide/command-line.md | 4 +- docs/guide/configuration.md | 4 +- docs/guide/diagnostics.md | 8 +-- docs/guide/durability/dead-letter-storage.md | 18 +++++- docs/guide/durability/idempotency.md | 4 +- docs/guide/durability/index.md | 4 +- .../durability/marten/ancillary-stores.md | 4 +- .../guide/durability/marten/event-sourcing.md | 58 ++++++++++++------- docs/guide/durability/marten/operations.md | 6 +- docs/guide/durability/marten/outbox.md | 4 +- docs/guide/durability/marten/subscriptions.md | 4 +- .../marten/transactional-middleware.md | 8 +-- docs/guide/durability/sagas.md | 12 ++-- docs/guide/extensions.md | 20 +++---- docs/guide/handlers/batching.md | 12 ++-- docs/guide/handlers/cascading.md | 44 +++++++------- docs/guide/handlers/discovery.md | 35 ++++++++--- docs/guide/handlers/error-handling.md | 12 ++-- docs/guide/handlers/index.md | 44 +++++++------- docs/guide/handlers/middleware.md | 40 ++++++------- docs/guide/handlers/multi-tenancy.md | 4 +- docs/guide/handlers/persistence.md | 4 +- docs/guide/handlers/return-values.md | 16 ++--- docs/guide/handlers/side-effects.md | 12 ++-- docs/guide/handlers/sticky.md | 4 +- docs/guide/handlers/timeout.md | 4 +- docs/guide/http/endpoints.md | 18 +++--- docs/guide/http/index.md | 4 +- docs/guide/http/integration.md | 14 ++--- docs/guide/http/marten.md | 17 ++++-- docs/guide/http/messaging.md | 20 +++---- docs/guide/http/metadata.md | 8 +-- docs/guide/http/middleware.md | 12 ++-- docs/guide/http/multi-tenancy.md | 20 +++---- docs/guide/http/policies.md | 8 +-- docs/guide/http/problemdetails.md | 4 +- docs/guide/http/security.md | 4 +- docs/guide/logging.md | 10 ++-- docs/guide/messages.md | 32 +++++----- docs/guide/messaging/expiration.md | 2 +- docs/guide/messaging/listeners.md | 4 +- docs/guide/messaging/message-bus.md | 22 +++---- docs/guide/messaging/subscriptions.md | 24 ++++---- docs/guide/messaging/transports/local.md | 28 ++++----- docs/guide/messaging/transports/mqtt.md | 17 ++---- .../rabbitmq/conventional-routing.md | 6 +- .../transports/rabbitmq/deadletterqueues.md | 6 +- .../messaging/transports/rabbitmq/index.md | 8 +-- .../transports/rabbitmq/interoperability.md | 12 ++-- .../transports/rabbitmq/listening.md | 4 +- .../transports/rabbitmq/multiple-brokers.md | 2 +- .../transports/rabbitmq/object-management.md | 41 +++++++++++-- .../transports/rabbitmq/publishing.md | 6 +- .../messaging/transports/rabbitmq/topics.md | 18 ++---- docs/guide/messaging/transports/tcp.md | 8 +-- docs/guide/migration.md | 4 +- docs/guide/runtime.md | 16 ++--- docs/guide/testing.md | 8 +-- docs/tutorials/cqrs-with-marten.md | 24 ++++---- docs/tutorials/from-mediatr.md | 4 +- docs/tutorials/getting-started.md | 16 ++--- docs/tutorials/mediator.md | 20 +++---- docs/tutorials/middleware.md | 16 ++--- docs/tutorials/ping-pong.md | 28 ++++----- docs/tutorials/railway-programming.md | 4 +- 66 files changed, 493 insertions(+), 423 deletions(-) diff --git a/docs/guide/basics.md b/docs/guide/basics.md index 29022f7ee..ee1ba1932 100644 --- a/docs/guide/basics.md +++ b/docs/guide/basics.md @@ -10,7 +10,7 @@ something happened. Just know that as far as Wolverine is concerned, those are r Here's a couple simple samples: - + ```cs // A "command" message public record DebitAccount(long AccountId, decimal Amount); @@ -18,14 +18,14 @@ public record DebitAccount(long AccountId, decimal Amount); // An "event" message public record AccountOverdrawn(long AccountId); ``` -snippet source | anchor +snippet source | anchor The next concept in Wolverine is a message handler, which is just a method that "knows" how to process an incoming message. Here's an extremely simple example: - + ```cs public static class DebitAccountHandler { @@ -35,7 +35,7 @@ public static class DebitAccountHandler } } ``` -snippet source | anchor +snippet source | anchor Wolverine can act as a completely local mediator tool that allows your code to invoke the handler for a message at any time without having diff --git a/docs/guide/command-line.md b/docs/guide/command-line.md index 60782c6b7..c2e73a456 100644 --- a/docs/guide/command-line.md +++ b/docs/guide/command-line.md @@ -5,7 +5,7 @@ tools. To get started, apply Oakton as the command line parser in your applicati sample application bootstrapping from Wolverine's [Getting Started](/tutorials/getting-started): - + ```cs using Oakton; using Quickstart; @@ -47,7 +47,7 @@ app.MapGet("/", () => Results.Redirect("/swagger")); // your Wolverine application return await app.RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor From this project's root in the command line terminal tool of your choice, type: diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md index 505bed92f..5b3552f80 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/configuration.md @@ -35,7 +35,7 @@ Below is a sample of adding Wolverine to an ASP.NET Core application that is boo `WebApplicationBuilder`: - + ```cs using Oakton; using Quickstart; @@ -77,7 +77,7 @@ app.MapGet("/", () => Results.Redirect("/swagger")); // your Wolverine application return await app.RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor ## "Headless" Applications diff --git a/docs/guide/diagnostics.md b/docs/guide/diagnostics.md index ec1356475..aa1d2068e 100644 --- a/docs/guide/diagnostics.md +++ b/docs/guide/diagnostics.md @@ -9,7 +9,7 @@ to utilize this command line integration, you need to apply Oakton as your comma sample `Program.cs` file: - + ```cs using Oakton; using Quickstart; @@ -51,7 +51,7 @@ app.MapGet("/", () => Results.Redirect("/swagger")); // your Wolverine application return await app.RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor ## Command Line Description @@ -142,14 +142,14 @@ Probably mostly for testing projects, you can verify that all the message handle application are in a valid state by executing this method: - + ```cs public static void assert_configuration_is_valid(IHost host) { host.AssertWolverineConfigurationIsValid(); } ``` -snippet source | anchor +snippet source | anchor Note that this method will attempt to generate and compile the source code for each message type and use [Lamar's own diff --git a/docs/guide/durability/dead-letter-storage.md b/docs/guide/durability/dead-letter-storage.md index a60e0a996..097e1f61b 100644 --- a/docs/guide/durability/dead-letter-storage.md +++ b/docs/guide/durability/dead-letter-storage.md @@ -33,7 +33,23 @@ so Wolverine does have an "opt in" feature to let old messages expire and be exp It's off by default (for backwards compatibility), but you can enable Wolverine to assign expiration times to dead letter queue messages persisted to durable storage like this: -snippet: sample_enabling_dead_letter_queue_expiration + + +```cs +using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + + // This is required + opts.Durability.DeadLetterQueueExpirationEnabled = true; + + // Default is 10 days. This is the retention period + opts.Durability.DeadLetterQueueExpiration = 3.Days(); + + }).StartAsync(); +``` +snippet source | anchor + Note that Wolverine will use the message's `DeliverBy` value as the expiration if that exists, otherwise, Wolverine will just add the `DeadLetterQueueExpiration` time to the current time. The actual stored messages are deleted by background diff --git a/docs/guide/durability/idempotency.md b/docs/guide/durability/idempotency.md index 74f4aeb1c..c417867fd 100644 --- a/docs/guide/durability/idempotency.md +++ b/docs/guide/durability/idempotency.md @@ -14,7 +14,7 @@ Instead of immediately deleting message storage for a successfully completed mes that message in storage for a default of 5 minutes to protect against duplicate incoming messages. To override that setting, you have this option: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -25,5 +25,5 @@ using var host = await Host.CreateDefaultBuilder() opts.Durability.KeepAfterMessageHandling = 10.Minutes(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/durability/index.md b/docs/guide/durability/index.md index 180d5a758..aab405fc6 100644 --- a/docs/guide/durability/index.md +++ b/docs/guide/durability/index.md @@ -27,7 +27,7 @@ getting lost en route. Consider this sample message handler from Wolverine's [AppWithMiddleware sample project](https://github.com/JasperFx/wolverine/tree/main/src/Samples/Middleware): - + ```cs [Transactional] public static async Task Handle( @@ -62,7 +62,7 @@ public static async Task Handle( new DeliveryOptions { DeliverWithin = 5.Seconds() }); } ``` -snippet source | anchor +snippet source | anchor The handler code above is committing changes to an `Account` in the underlying database and potentially sending out additional messages based on the state of the `Account`. diff --git a/docs/guide/durability/marten/ancillary-stores.md b/docs/guide/durability/marten/ancillary-stores.md index 5dd9538f5..59d9e7c78 100644 --- a/docs/guide/durability/marten/ancillary-stores.md +++ b/docs/guide/durability/marten/ancillary-stores.md @@ -79,7 +79,7 @@ Now, moving to message handlers or HTTP endpoints, you will have to explicitly t individual messages with the `[MartenStore(store type)]` attribute like this simple example below: - + ```cs // This will use a Marten session from the // IPlayerStore rather than the main IDocumentStore @@ -93,7 +93,7 @@ public static class PlayerMessageHandler } } ``` -snippet source | anchor +snippet source | anchor ::: info diff --git a/docs/guide/durability/marten/event-sourcing.md b/docs/guide/durability/marten/event-sourcing.md index 75985a4a1..3f1577f6a 100644 --- a/docs/guide/durability/marten/event-sourcing.md +++ b/docs/guide/durability/marten/event-sourcing.md @@ -23,7 +23,7 @@ And then lastly, you're going to want some resiliency and selective retry capabi Let's just right into an example order management system. I'm going to model the order workflow with this aggregate model: - + ```cs public class Item { @@ -67,19 +67,19 @@ public class Order } } ``` -snippet source | anchor +snippet source | anchor At a minimum, we're going to want a command handler for this command message that marks an order item as ready to ship and then evaluates whether or not based on the current state of the `Order` aggregate whether or not the logical order is ready to be shipped out: - + ```cs // OrderId refers to the identity of the Order aggregate public record MarkItemReady(Guid OrderId, string ItemName, int Version); ``` -snippet source | anchor +snippet source | anchor In the code above, we're also utilizing Wolverine's [outbox messaging](/guide/durability/) support to both order and guarantee the delivery of a `ShipOrder` message when @@ -88,7 +88,7 @@ the Marten transaction Before getting into Wolverine middleware strategies, let's first build out an MVC controller method for the command above: - + ```cs [HttpPost("/orders/itemready")] public async Task Post( @@ -139,7 +139,7 @@ public async Task Post( await session.SaveChangesAsync(); } ``` -snippet source | anchor +snippet source | anchor Hopefully, that code is easy to understand, but there's some potentially repetitive code @@ -152,7 +152,7 @@ pattern with Marten using the `[AggregateHandler]` middleware. Using that middleware, we get this slimmer code: - + ```cs [AggregateHandler] public static IEnumerable Handle(MarkItemReady command, Order order) @@ -179,7 +179,7 @@ public static IEnumerable Handle(MarkItemReady command, Order order) } } ``` -snippet source | anchor +snippet source | anchor In the case above, Wolverine is wrapping middleware around our basic command handler to @@ -238,7 +238,7 @@ The Marten workflow command handler method signature needs to follow these rules in the Marten `IEventStream` type (`IEventStream`). There is an example of that usage below: - + ```cs [AggregateHandler] public static void Handle(OrderEventSourcingSample.MarkItemReady command, IEventStream stream) @@ -267,7 +267,7 @@ public static void Handle(OrderEventSourcingSample.MarkItemReady command, IEvent } } ``` -snippet source | anchor +snippet source | anchor Just as in other Wolverine [message handlers](/guide/handlers/), you can use @@ -288,7 +288,7 @@ As for the return values from these handler methods, you can use: Here's an alternative to the `MarkItemReady` handler that uses `Events`: - + ```cs [AggregateHandler] public static async Task<(Events, OutgoingMessages)> HandleAsync(MarkItemReady command, Order order, ISomeService service) @@ -328,7 +328,7 @@ public static async Task<(Events, OutgoingMessages)> HandleAsync(MarkItemReady c return (events, messages); } ``` -snippet source | anchor +snippet source | anchor @@ -341,12 +341,12 @@ where the `OrderId` property is assumed to be the identity of the `Order` aggreg by appending "Id" to the aggregate type name (it's not case sensitive if you were wondering): - + ```cs // OrderId refers to the identity of the Order aggregate public record MarkItemReady(Guid OrderId, string ItemName, int Version); ``` -snippet source | anchor +snippet source | anchor Or if you want to use a different member, bypass the convention, or just don't like conventional @@ -354,7 +354,7 @@ magic, you can decorate a public member on the command class with Marten's `[Identity]` attribute like so: - + ```cs public class MarkItemReady { @@ -365,7 +365,7 @@ public class MarkItemReady public string ItemName { get; init; } } ``` -snippet source | anchor +snippet source | anchor ## Forwarding Events @@ -410,7 +410,7 @@ taking the `MarkItemReady` command handler we've used earlier in this guide and produces a response of the latest aggregate: - + ```cs [AggregateHandler] public static ( @@ -448,7 +448,7 @@ public static ( return (new UpdatedAggregate(), events); } ``` -snippet source | anchor +snippet source | anchor Note the usage of the `Wolverine.Marten.UpdatedAggregate` response in the handler. That type by itself is just a directive @@ -456,7 +456,7 @@ to Wolverine to generate the necessary code to call `FetchLatest` and respond wi us to use the command in a mediator usage like so: - + ```cs public static Task update_and_get_latest(IMessageBus bus, MarkItemReady command) { @@ -466,7 +466,7 @@ public static Task update_and_get_latest(IMessageBus bus, MarkItemReady c return bus.InvokeAsync(command); } ``` -snippet source | anchor +snippet source | anchor ] Likewise, you can use `UpdatedAggregate` as the response body of an HTTP endpoint with Wolverine.HTTP [as shown here](/guide/http/marten.html#responding-with-the-updated-aggregate~~~~). @@ -519,7 +519,23 @@ projections. If you want to inject the current state of an event sourced aggregate as a parameter into a message handler method strictly for information and don't need the heavier "aggregate handler workflow," use the `[ReadAggregate]` attribute like this: -snippet: sample_using_ReadAggregate_in_messsage_handlers + + +```cs +public record FindAggregate(Guid Id); + +public static class FindLettersHandler +{ + // This is admittedly just some weak sauce testing support code + public static LetterAggregateEnvelope Handle( + FindAggregate command, + [ReadAggregate] LetterAggregate aggregate) + + => new LetterAggregateEnvelope(aggregate); +} +``` +snippet source | anchor + If the aggregate doesn't exist, the HTTP request will stop with a 404 status code. The aggregate/stream identity is found with the same rules as the `[Entity]` or `[Aggregate]` attributes: diff --git a/docs/guide/durability/marten/operations.md b/docs/guide/durability/marten/operations.md index 7e1f2b1bb..36427497e 100644 --- a/docs/guide/durability/marten/operations.md +++ b/docs/guide/durability/marten/operations.md @@ -19,7 +19,7 @@ The `Wolverine.Marten` library includes some helpers for Wolverine [side effects Marten with the `IMartenOp` interface: - + ```cs /// /// Interface for any kind of Marten related side effect @@ -29,7 +29,7 @@ public interface IMartenOp : ISideEffect void Execute(IDocumentSession session); } ``` -snippet source | anchor +snippet source | anchor The built in side effects can all be used from the `MartenOps` static class like this HTTP endpoint example: @@ -94,7 +94,7 @@ public static IEnumerable Handle(AppendManyNamedDocuments command) } } ``` -snippet source | anchor +snippet source | anchor Wolverine will pick up on any return type that can be cast to `IEnumerable`, so for example: diff --git a/docs/guide/durability/marten/outbox.md b/docs/guide/durability/marten/outbox.md index f2b1eb834..99de4784a 100644 --- a/docs/guide/durability/marten/outbox.md +++ b/docs/guide/durability/marten/outbox.md @@ -75,7 +75,7 @@ The Wolverine outbox is also usable from within ASP.Net Core (really any code) c handling code would be: - + ```cs public class CreateOrderController : ControllerBase { @@ -102,7 +102,7 @@ public class CreateOrderController : ControllerBase } } ``` -snippet source | anchor +snippet source | anchor From a Minimal API, that could be this: diff --git a/docs/guide/durability/marten/subscriptions.md b/docs/guide/durability/marten/subscriptions.md index 7a3ca1e0c..d7e5a5b1e 100644 --- a/docs/guide/durability/marten/subscriptions.md +++ b/docs/guide/durability/marten/subscriptions.md @@ -199,7 +199,7 @@ example usage where I'm using [event carried state transfer](https://martinfowle publish batches of reference data about customers being activated or deactivated within our system: - + ```cs public record CompanyActivated(string Name); @@ -266,7 +266,7 @@ public class CompanyTransferSubscription : BatchSubscription } } ``` -snippet source | anchor +snippet source | anchor And the related code to register this subscription: diff --git a/docs/guide/durability/marten/transactional-middleware.md b/docs/guide/durability/marten/transactional-middleware.md index 7901323b3..980d0eb00 100644 --- a/docs/guide/durability/marten/transactional-middleware.md +++ b/docs/guide/durability/marten/transactional-middleware.md @@ -102,7 +102,7 @@ name ends with "Command" to use the Marten transaction middleware. You could acc with a handler policy like this: - + ```cs public class CommandsAreTransactional : IHandlerPolicy { @@ -116,13 +116,13 @@ public class CommandsAreTransactional : IHandlerPolicy } } ``` -snippet source | anchor +snippet source | anchor Then add the policy to your application like this: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -131,5 +131,5 @@ using var host = await Host.CreateDefaultBuilder() opts.Policies.Add(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/durability/sagas.md b/docs/guide/durability/sagas.md index e241afe31..afad39084 100644 --- a/docs/guide/durability/sagas.md +++ b/docs/guide/durability/sagas.md @@ -24,7 +24,7 @@ Jumping right into an example, consider a very simple order management service t For the moment, I’m going to ignore the underlying persistence and just focus on the Wolverine message handlers to implement the order saga workflow with this simplistic saga code: - + ```cs public record StartOrder(string OrderId); @@ -74,7 +74,7 @@ public class Order : Saga } ``` -snippet source | anchor +snippet source | anchor A few explanatory notes on this code before we move on to detailed documentation: @@ -254,7 +254,7 @@ identity. In order of precedence, Wolverine first looks for a member decorated w `[SagaIdentity]` attribute like this: - + ```cs public class ToyOnTray { @@ -265,7 +265,7 @@ public class ToyOnTray [SagaIdentity] public int OrderId { get; set; } } ``` -snippet source | anchor +snippet source | anchor Next, Wolverine looks for a member named "{saga type name}Id." In the case of our `Order` @@ -443,13 +443,13 @@ Wolverine really wants you to be able to use pure functions as much as possible, for any logical message that will be scheduled in the future like so: - + ```cs // This message will always be scheduled to be delivered after // a one minute delay public record OrderTimeout(string Id) : TimeoutMessage(1.Minutes()); ``` -snippet source | anchor +snippet source | anchor That `OrderTimeout` message can be published with normal cascaded messages (or by calling `IMessageBus.PublishAsync()` if you prefer) diff --git a/docs/guide/extensions.md b/docs/guide/extensions.md index 204b56361..596b4cae8 100644 --- a/docs/guide/extensions.md +++ b/docs/guide/extensions.md @@ -9,7 +9,7 @@ in the IoC container at bootstrapping time. Wolverine supports the concept of extensions for modularizing Wolverine configuration with implementations of the `IWolverineExtension` interface: - + ```cs /// /// Use to create loadable extensions to Wolverine applications @@ -23,13 +23,13 @@ public interface IWolverineExtension void Configure(WolverineOptions options); } ``` -snippet source | anchor +snippet source | anchor Here's a sample: - + ```cs public class SampleExtension : IWolverineExtension { @@ -44,7 +44,7 @@ public class SampleExtension : IWolverineExtension } } ``` -snippet source | anchor +snippet source | anchor Extensions can be applied programmatically against the `WolverineOptions` like this: @@ -99,7 +99,7 @@ await using var host = await AlbaHost.For(x => Behind the scenes, Wolverine has a small extension like this: - + ```cs internal class DisableExternalTransports : IWolverineExtension { @@ -109,7 +109,7 @@ internal class DisableExternalTransports : IWolverineExtension } } ``` -snippet source | anchor +snippet source | anchor And that extension is just added to the application's IoC container at test bootstrapping time like this: @@ -179,7 +179,7 @@ There is also any option for creating Wolverine extensions that need to use asyn the `WolverineOptions` using the `IAsyncWolverineExtension` library. A sample is shown below: - + ```cs public class SampleAsyncExtension : IAsyncWolverineExtension { @@ -203,7 +203,7 @@ public class SampleAsyncExtension : IAsyncWolverineExtension } } ``` -snippet source | anchor +snippet source | anchor Which can be added to your application with this extension method on `IServiceCollection`: @@ -234,7 +234,7 @@ this method in your `Program` file *after* building the `WebApplication`, but be `MapWolverineEndpoints()` like so: - + ```cs var app = builder.Build(); @@ -242,7 +242,7 @@ var app = builder.Build(); // you will need to explicitly call this *before* MapWolverineEndpoints() await app.Services.ApplyAsyncWolverineExtensions(); ``` -snippet source | anchor +snippet source | anchor ## Wolverine Plugin Modules diff --git a/docs/guide/handlers/batching.md b/docs/guide/handlers/batching.md index e0d7e42c6..0554fed83 100644 --- a/docs/guide/handlers/batching.md +++ b/docs/guide/handlers/batching.md @@ -172,7 +172,7 @@ To teach Wolverine how to batch up our `SubTaskCompleted` messages into our cust type: - + ```cs /// /// Plugin strategy for creating custom grouping of messages @@ -193,13 +193,13 @@ public interface IMessageBatcher Type BatchMessageType { get; } } ``` -snippet source | anchor +snippet source | anchor A custom implementation of that interface in this case would look like this: - + ```cs public class SubTaskCompletedBatcher : IMessageBatcher { @@ -231,13 +231,13 @@ public class SubTaskCompletedBatcher : IMessageBatcher public Type BatchMessageType => typeof(SubTaskCompletedBatch); } ``` -snippet source | anchor +snippet source | anchor And of course, this doesn't work without a matching message handler for our custom message type: - + ```cs public static class SubTaskCompletedBatchHandler { @@ -254,7 +254,7 @@ public static class SubTaskCompletedBatchHandler } } ``` -snippet source | anchor +snippet source | anchor And finally, we need to tell Wolverine about the batching and the strategy for batching the `SubTaskCompleted` diff --git a/docs/guide/handlers/cascading.md b/docs/guide/handlers/cascading.md index de7244903..4d5d8a2c0 100644 --- a/docs/guide/handlers/cascading.md +++ b/docs/guide/handlers/cascading.md @@ -17,7 +17,7 @@ maybe you need to trigger a subsequent action, or send out additional messages t your handler class use the `IMessageContext` interface as shown in this sample: - + ```cs public class NoCascadingHandler { @@ -36,14 +36,14 @@ public class NoCascadingHandler } } ``` -snippet source | anchor +snippet source | anchor The code above certainly works and this is consistent with most of the competing service bus tools. However, Wolverine supports the concept of _cascading messages_ that allow you to automatically send out objects returned from your handler methods without having to use `IMessageContext` as shown below: - + ```cs public class CascadingHandler { @@ -53,7 +53,7 @@ public class CascadingHandler } } ``` -snippet source | anchor +snippet source | anchor When Wolverine executes `CascadingHandler.Consume(MyMessage)`, it "knows" that the `MyResponse` return value should be sent through the @@ -83,7 +83,7 @@ Sometimes you'll want to explicitly send messages to specific endpoints rather t You can still use cascading messages to an endpoint by name or by the destination `Uri` like so: - + ```cs public class ManuallyRoutedResponseHandler { @@ -97,7 +97,7 @@ public class ManuallyRoutedResponseHandler } } ``` -snippet source | anchor +snippet source | anchor You can also add optional `DeliveryOptions` to the outgoing messages to fine tune how the message is to be published. @@ -115,7 +115,7 @@ helps Wolverine "know" that these messages should be cascaded after the initial The usage of this is shown below: - + ```cs public static OutgoingMessages Handle(Incoming incoming) { @@ -141,7 +141,7 @@ public static OutgoingMessages Handle(Incoming incoming) return messages; } ``` -snippet source | anchor +snippet source | anchor Do note that the value of `OutgoingMessages` is probably greatest when being used in a tuple response from a handler that's a mix @@ -192,7 +192,7 @@ Let's say that we have two running service bus nodes named "Sender" and "Receive is called from the "Sender" node: - + ```cs public class Requester { @@ -209,13 +209,13 @@ public class Requester } } ``` -snippet source | anchor +snippet source | anchor and inside Receiver we have this code: - + ```cs public class CascadingHandler { @@ -225,7 +225,7 @@ public class CascadingHandler } } ``` -snippet source | anchor +snippet source | anchor Assuming that `MyMessage` is configured to be sent to "Receiver," the following steps take place: @@ -244,7 +244,7 @@ different types of cascading messages based on some kind of logic, you can still be `object` like this sample shown below: - + ```cs public class ConditionalResponseHandler { @@ -263,7 +263,7 @@ public class ConditionalResponseHandler } } ``` -snippet source | anchor +snippet source | anchor @@ -272,7 +272,7 @@ public class ConditionalResponseHandler You may want to raise a delayed or scheduled response. In this case you will need to return an <[linkto:documentation/integration/customizing_envelopes;title=Envelope]> for the response as shown below: - + ```cs public class ScheduledResponseHandler { @@ -288,7 +288,7 @@ public class ScheduledResponseHandler } } ``` -snippet source | anchor +snippet source | anchor ## Multiple Cascading Messages @@ -298,7 +298,7 @@ cast to `IEnumerable`, and Wolverine will treat each element as a separa An empty enumerable is just ignored. - + ```cs public class MultipleResponseHandler { @@ -312,7 +312,7 @@ public class MultipleResponseHandler } } ``` -snippet source | anchor +snippet source | anchor @@ -325,7 +325,7 @@ C# tuples to better denote the cascading message types. This handler cascading a pair of messages: - + ```cs public class MultipleResponseHandler { @@ -339,13 +339,13 @@ public class MultipleResponseHandler } } ``` -snippet source | anchor +snippet source | anchor can be rewritten with C# 7 tuples to: - + ```cs public class TupleResponseHandler { @@ -357,7 +357,7 @@ public class TupleResponseHandler } } ``` -snippet source | anchor +snippet source | anchor The sample above still treats both `GoNorth` and the `ScheduledResponse` as cascading messages. The Wolverine team thinks that the diff --git a/docs/guide/handlers/discovery.md b/docs/guide/handlers/discovery.md index f741532be..75a1183c8 100644 --- a/docs/guide/handlers/discovery.md +++ b/docs/guide/handlers/discovery.md @@ -128,7 +128,7 @@ In all cases, Wolverine assumes that the first argument is the incoming message. To make that concrete, here are some valid handler method signatures: - + ```cs [WolverineHandler] public class ValidMessageHandlers @@ -181,7 +181,7 @@ public class ValidMessageHandlers } } ``` -snippet source | anchor +snippet source | anchor The valid method names are: @@ -214,7 +214,7 @@ You can completely turn off any automatic discovery of message handlers through using this syntax in your `WolverineOptions`: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -223,7 +223,7 @@ using var host = await Host.CreateDefaultBuilder() opts.DisableConventionalDiscovery(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Replacing the Handler Discovery Rules @@ -231,7 +231,24 @@ using var host = await Host.CreateDefaultBuilder() You can completely replace Wolverine's handler type discovery by first disabling the conventional handler discovery, then adding all new rules like this: -snippet: sample_replacing_handler_discovery_rules + + +```cs +using var host = await Host.CreateDefaultBuilder() + .UseWolverine(opts => + { + // Turn off Wolverine's built in handler discovery + opts.DisableConventionalDiscovery(); + + // And replace the scanning with your own special discovery: + opts.Discovery.CustomizeHandlerDiscovery(q => + { + q.Includes.WithNameSuffix("Listener"); + }); + }).StartAsync(); +``` +snippet source | anchor + ## Explicitly Ignoring Methods @@ -239,7 +256,7 @@ You can force Wolverine to disregard a candidate message handler action at eithe level by using the `[WolverineIgnore]` attribute like this: - + ```cs public class NetflixHandler : IMovieSink { @@ -287,7 +304,7 @@ public class BlockbusterHandler } } ``` -snippet source | anchor +snippet source | anchor @@ -304,7 +321,7 @@ from those naming conventions you can either supplement the handler discovery or At a minimum, you can disable the built in discovery, add additional type filtering criteria, or register specific handler classes with the code below: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -327,5 +344,5 @@ using var host = await Host.CreateDefaultBuilder() .IncludeType(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/handlers/error-handling.md b/docs/guide/handlers/error-handling.md index 895e63de7..b9d7bf785 100644 --- a/docs/guide/handlers/error-handling.md +++ b/docs/guide/handlers/error-handling.md @@ -227,7 +227,7 @@ public class MyErrorCausingHandler To specify global error handling rules, use the fluent interface directly on `WolverineOptions.Handlers` as shown below: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -242,7 +242,7 @@ using var host = await Host.CreateDefaultBuilder() .ScheduleRetry(5.Seconds()); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor TODO -- link to chain policies, after that exists:) @@ -251,7 +251,7 @@ Lastly, you can use chain policies to add error handling policies to a selected a sample policy that applies an error handling policy based on `SqlException` errors for all message types from a certain namespace: - + ```cs // This error policy will apply to all message types in the namespace // 'MyApp.Messages', and add a "requeue on SqlException" to all of these @@ -267,7 +267,7 @@ public class ErrorHandlingPolicy : IHandlerPolicy } } ``` -snippet source | anchor +snippet source | anchor ## Exception Filtering @@ -324,7 +324,7 @@ Optionally, you can implement a new type to handle this same custom logic by subclassing the `Wolverine.ErrorHandling.UserDefinedContinuation` type like so: - + ```cs public class ShippingOrderFailurePolicy : UserDefinedContinuation { @@ -344,7 +344,7 @@ public class ShippingOrderFailurePolicy : UserDefinedContinuation } } ``` -snippet source | anchor +snippet source | anchor and register that secondary action like this: diff --git a/docs/guide/handlers/index.md b/docs/guide/handlers/index.md index 744237c20..59dac70be 100644 --- a/docs/guide/handlers/index.md +++ b/docs/guide/handlers/index.md @@ -37,14 +37,14 @@ allow *you* to just write plain old .NET code without any framework specific art Back to the handler code, at the point which you pass a new message into Wolverine like so: - + ```cs public static async Task publish_command(IMessageBus bus) { await bus.PublishAsync(new MyMessage()); } ``` -snippet source | anchor +snippet source | anchor Between the call to `IMessageBus.PublishAsync()` and `MyMessageHandler.Handle(MyMessage)` there's a couple things @@ -58,7 +58,7 @@ going on: Before diving into the exact rules for message handlers, here are some valid handler methods: - + ```cs [WolverineHandler] public class ValidMessageHandlers @@ -111,7 +111,7 @@ public class ValidMessageHandlers } } ``` -snippet source | anchor +snippet source | anchor It's also valid to use class instances with constructor arguments for your handlers: @@ -185,7 +185,7 @@ Now though, you can flip this switch in one place to ensure that Wolverine alway into completely separate Wolverine message handlers and message subscriptions: - + ```cs using var host = Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -194,7 +194,7 @@ using var host = Host.CreateDefaultBuilder() opts.MultipleHandlerBehavior = MultipleHandlerBehavior.Separated; }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor This makes a couple changes. For example, let's say that you have these handlers for the same message type of `MyApp.Orders.OrderCreated`: @@ -246,7 +246,7 @@ Some add ons or middleware add other possibilities as well. Handler methods can be instance methods on handler classes if it's desirable to scope the handler object to the message: - + ```cs public class ExampleHandler { @@ -262,7 +262,7 @@ public class ExampleHandler } } ``` -snippet source | anchor +snippet source | anchor When using instance methods, the containing handler type will be scoped to a single message and be @@ -270,7 +270,7 @@ disposed afterward. In the case of instance methods, it's perfectly legal to use to resolve IoC registered dependencies as shown below: - + ```cs public class ServiceUsingHandler { @@ -290,7 +290,7 @@ public class ServiceUsingHandler } } ``` -snippet source | anchor +snippet source | anchor ::: tip @@ -301,7 +301,7 @@ improvement by avoiding the need to create and garbage collect new objects at ru As an alternative, you can also use static methods as message handlers: - + ```cs public static class ExampleHandler { @@ -317,7 +317,7 @@ public static class ExampleHandler } } ``` -snippet source | anchor +snippet source | anchor The handler classes can be static classes as well. This technique gets much more useful when combined with Wolverine's @@ -332,7 +332,7 @@ arguments that will be passed into your method by Wolverine when a new message i Below is an example action method that takes in a dependency on an `IDocumentSession` from [Marten](https://jasperfx.github.io/marten/): - + ```cs public static class MethodInjectionHandler { @@ -345,7 +345,7 @@ public static class MethodInjectionHandler } } ``` -snippet source | anchor +snippet source | anchor So, what can be injected as an argument to your message handler? @@ -383,7 +383,7 @@ data for both the order itself and the customer information in order to figure o Using Wolverine's compound handler feature, that might look like this: - + ```cs public static class ShipOrderHandler { @@ -413,7 +413,7 @@ public static class ShipOrderHandler } } ``` -snippet source | anchor +snippet source | anchor The naming conventions for what Wolverine will consider to be either a "before" or "after" method is shown below: @@ -445,7 +445,7 @@ To access the `Envelope` for the current message being handled in your message h argument like this: - + ```cs public class EnvelopeUsingHandler { @@ -456,7 +456,7 @@ public class EnvelopeUsingHandler } } ``` -snippet source | anchor +snippet source | anchor @@ -467,7 +467,7 @@ or maybe to enqueue local commands within the current outbox scope, just take in like in this example: - + ```cs using Messages; using Microsoft.Extensions.Logging; @@ -484,8 +484,8 @@ public class PingHandler } } ``` -snippet source | anchor - +snippet source | anchor + ```cs public static class PingHandler { @@ -514,6 +514,6 @@ public static class PingHandler } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/handlers/middleware.md b/docs/guide/handlers/middleware.md index ee8979a63..6ca261fb2 100644 --- a/docs/guide/handlers/middleware.md +++ b/docs/guide/handlers/middleware.md @@ -50,7 +50,7 @@ to HTTP endpoints, see [HTTP endpoint middleware](/guide/http/middleware). As an example middleware using Wolverine's conventional approach, here's the stopwatch functionality from above: - + ```cs public class StopwatchMiddleware { @@ -69,7 +69,7 @@ public class StopwatchMiddleware } } ``` -snippet source | anchor +snippet source | anchor and that can be added to our application at bootstrapping time like this: @@ -174,7 +174,7 @@ Here's an example from the [custom middleware tutorial](/tutorials/middleware) t by the incoming message and aborts the message processing if it is not found: - + ```cs // This is *a* way to build middleware in Wolverine by basically just // writing functions/methods. There's a naming convention that @@ -207,7 +207,7 @@ public static class AccountLookupMiddleware } } ``` -snippet source | anchor +snippet source | anchor Notice that the middleware above uses a tuple as the return value so that it can both pass an `Account` entity to the inner handler and also @@ -285,14 +285,14 @@ public static class MaybeBadThing2Handler Let's say that some of our message types implement this interface: - + ```cs public interface IAccountCommand { Guid AccountId { get; } } ``` -snippet source | anchor +snippet source | anchor We can apply the `AccountMiddleware` from the section above to only these message types by telling Wolverine to only apply this middleware @@ -364,7 +364,7 @@ For more advanced usage, you can drop down to the JasperFx.CodeGeneration `Frame The first step is to create a JasperFx.CodeGeneration `Frame` class that generates that code around the inner message or HTTP handler: - + ```cs public class StopwatchFrame : SyncFrame { @@ -410,7 +410,7 @@ public class StopwatchFrame : SyncFrame } } ``` -snippet source | anchor +snippet source | anchor @@ -420,7 +420,7 @@ To attach our `StopwatchFrame` as middleware to any route or message handler, we `ModifyChainAttribute` class as shown below: - + ```cs public class StopwatchAttribute : ModifyChainAttribute { @@ -430,7 +430,7 @@ public class StopwatchAttribute : ModifyChainAttribute } } ``` -snippet source | anchor +snippet source | anchor This attribute can now be placed either on a specific HTTP route endpoint method or message handler method to **only** apply to @@ -439,7 +439,7 @@ that specific action, or it can be placed on a `Handler` or `Endpoint` class to Here's an example: - + ```cs public class ClockedEndpoint { @@ -450,7 +450,7 @@ public class ClockedEndpoint } } ``` -snippet source | anchor +snippet source | anchor Now, when the application is bootstrapped, this is the code that would be generated to handle the "GET /clocked" route: @@ -499,7 +499,7 @@ Again, please go easy with this feature and try not to shoot yourself in the foo You can register user-defined policies that apply to all chains or some subset of chains. For message handlers, implement this interface: - + ```cs /// /// Use to apply your own conventions or policies to message handlers @@ -515,13 +515,13 @@ public interface IHandlerPolicy : IWolverinePolicy void Apply(IReadOnlyList chains, GenerationRules rules, IServiceContainer container); } ``` -snippet source | anchor +snippet source | anchor Here's a simple sample that registers middleware on each handler chain: - + ```cs public class WrapWithSimple : IHandlerPolicy { @@ -531,18 +531,18 @@ public class WrapWithSimple : IHandlerPolicy } } ``` -snippet source | anchor +snippet source | anchor Then register your custom `IHandlerPolicy` with a Wolverine application like this: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => { opts.Policies.Add(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Using Configure(chain) Methods @@ -577,7 +577,7 @@ public static void Configure(HandlerChain chain) Here's an example of this being used from Wolverine's test suite: - + ```cs public class CustomizedHandler { @@ -598,7 +598,7 @@ public class CustomizedHandler } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/handlers/multi-tenancy.md b/docs/guide/handlers/multi-tenancy.md index 717f8f76c..00e186671 100644 --- a/docs/guide/handlers/multi-tenancy.md +++ b/docs/guide/handlers/multi-tenancy.md @@ -76,7 +76,7 @@ To that end, you can inject the `Wolverine.Persistence.TenantId` into any Wolver to get easy access to the tenant id: - + ```cs /// /// Strong typed identifier for the tenant id within a Wolverine message handler @@ -94,7 +94,7 @@ public record TenantId(string Value) public bool IsEmpty() => Value.IsEmpty() || Value == DefaultTenantId; } ``` -snippet source | anchor +snippet source | anchor There's really nothing to it other than just pulling that type in as a parameter argument to a message handler: diff --git a/docs/guide/handlers/persistence.md b/docs/guide/handlers/persistence.md index 18e0844d5..2e4b5b010 100644 --- a/docs/guide/handlers/persistence.md +++ b/docs/guide/handlers/persistence.md @@ -112,7 +112,7 @@ or request body, you can specify the identity value source through the `EntityAt to one of these values: - + ```cs public enum ValueSource { @@ -132,7 +132,7 @@ public enum ValueSource RouteValue } ``` -snippet source | anchor +snippet source | anchor Some other facts to know about `[Entity]` usage: diff --git a/docs/guide/handlers/return-values.md b/docs/guide/handlers/return-values.md index 4769287d1..a8650cc45 100644 --- a/docs/guide/handlers/return-values.md +++ b/docs/guide/handlers/return-values.md @@ -54,7 +54,7 @@ For an example, let's say that you want to isolate the [side effect](https://en. methods by returning a custom return value called `WriteFile`: - + ```cs // This has to be public btw public record WriteFile(string Path, string Contents) @@ -65,8 +65,8 @@ public record WriteFile(string Path, string Contents) } } ``` -snippet source | anchor - +snippet source | anchor + ```cs // ISideEffect is a Wolverine marker interface public class WriteFile : ISideEffect @@ -92,7 +92,7 @@ public class WriteFile : ISideEffect } } ``` -snippet source | anchor +snippet source | anchor And now, let's teach Wolverine to call the `WriteAsync()` method on each `WriteFile` that is returned from a message handler @@ -100,7 +100,7 @@ at runtime instead of Wolverine using the default policy of treating it as a cas to write a custom `IChainPolicy` like so: - + ```cs internal class WriteFilePolicy : IChainPolicy { @@ -133,17 +133,17 @@ internal class WriteFilePolicy : IChainPolicy } } ``` -snippet source | anchor +snippet source | anchor and lastly, I'll register that policy in my Wolverine application at configuration time: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => { opts.Policies.Add(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/handlers/side-effects.md b/docs/guide/handlers/side-effects.md index 0319ebaf2..e9f684451 100644 --- a/docs/guide/handlers/side-effects.md +++ b/docs/guide/handlers/side-effects.md @@ -24,7 +24,7 @@ irritating to use in tests). First off, I'm going to create a new "side effect" type for writing a file like this: - + ```cs // This has to be public btw public record WriteFile(string Path, string Contents) @@ -35,8 +35,8 @@ public record WriteFile(string Path, string Contents) } } ``` -snippet source | anchor - +snippet source | anchor + ```cs // ISideEffect is a Wolverine marker interface public class WriteFile : ISideEffect @@ -62,13 +62,13 @@ public class WriteFile : ISideEffect } } ``` -snippet source | anchor +snippet source | anchor And the matching message type, message handler, and a settings class for configuration: - + ```cs // An options class public class PathSettings @@ -87,7 +87,7 @@ public class RecordTextHandler } } ``` -snippet source | anchor +snippet source | anchor At runtime, Wolverine is generating this code to handle the `RecordText` message: diff --git a/docs/guide/handlers/sticky.md b/docs/guide/handlers/sticky.md index 41e22dc67..5955a268e 100644 --- a/docs/guide/handlers/sticky.md +++ b/docs/guide/handlers/sticky.md @@ -21,11 +21,11 @@ be handled completely separately by two different handlers performing two differ message as an input. - + ```cs public class StickyMessage; ``` -snippet source | anchor +snippet source | anchor And we're going to handle that `StickyMessage` message separately with two different handler types: diff --git a/docs/guide/handlers/timeout.md b/docs/guide/handlers/timeout.md index 640ae62ce..ee2087e75 100644 --- a/docs/guide/handlers/timeout.md +++ b/docs/guide/handlers/timeout.md @@ -24,10 +24,10 @@ To override the message timeout on a message type by message type basis, you can attribute as shown below: - + ```cs [MessageTimeout(1)] public async Task Handle(PotentiallySlowMessage message, CancellationToken cancellationToken) ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/http/endpoints.md b/docs/guide/http/endpoints.md index 6266049ee..375c9b095 100644 --- a/docs/guide/http/endpoints.md +++ b/docs/guide/http/endpoints.md @@ -154,7 +154,7 @@ to use any return values as a the endpoint response and to return an empty respo code. Here's an example from the tests: - + ```cs [AggregateHandler] [WolverinePost("/orders/ship"), EmptyResponse] @@ -167,7 +167,7 @@ public static OrderShipped Ship(ShipOrder command, Order order) return new OrderShipped(); } ``` -snippet source | anchor +snippet source | anchor ## JSON Handling @@ -200,9 +200,9 @@ The `IResult` mechanics are applied to the return value of any type that can be Wolverine will execute an ASP.Net Core `IResult` object returned from an HTTP endpoint method. - + ```cs -[WolverineGet("/choose/color")] +[WolverinePost("/choose/color")] public IResult Redirect(GoToColor request) { switch (request.Color) @@ -218,7 +218,7 @@ public IResult Redirect(GoToColor request) } } ``` -snippet source | anchor +snippet source | anchor @@ -263,7 +263,7 @@ There's actually a way to customize how Wolverine handles parameters in HTTP end To do so, you'd need to write an implementation of the `IParameterStrategy` interface from Wolverine.Http: - + ```cs /// /// Apply custom handling to a Wolverine.Http endpoint/chain based on a parameter within the @@ -275,14 +275,14 @@ public interface IParameterStrategy bool TryMatch(HttpChain chain, IServiceContainer container, ParameterInfo parameter, out Variable? variable); } ``` -snippet source | anchor +snippet source | anchor As an example, let's say that you want any parameter of type `DateTimeOffset` that's named "now" to receive the current system time. To do that, we can write this class: - + ```cs public class NowParameterStrategy : IParameterStrategy { @@ -301,7 +301,7 @@ public class NowParameterStrategy : IParameterStrategy } } ``` -snippet source | anchor +snippet source | anchor and register that strategy within our `MapWolverineEndpoints()` set up like so: diff --git a/docs/guide/http/index.md b/docs/guide/http/index.md index eee6f06ae..f015782f3 100644 --- a/docs/guide/http/index.md +++ b/docs/guide/http/index.md @@ -64,7 +64,7 @@ Okay, but we still need to expose a web service endpoint for this functionality. as a "mediator" tool like so: - + ```cs public class TodoController : ControllerBase { @@ -81,7 +81,7 @@ public class TodoController : ControllerBase } } ``` -snippet source | anchor +snippet source | anchor Or we could do the same thing with Minimal API: diff --git a/docs/guide/http/integration.md b/docs/guide/http/integration.md index 0d0c0386e..358fa6337 100644 --- a/docs/guide/http/integration.md +++ b/docs/guide/http/integration.md @@ -120,13 +120,13 @@ public async Task hello_world() result.ReadAsText().ShouldBe("Hello."); } ``` -snippet source | anchor +snippet source | anchor Moving on to the actual `Todo` problem domain, let's assume we've got a class like this: - + ```cs public class Todo { @@ -135,7 +135,7 @@ public class Todo public bool IsComplete { get; set; } } ``` -snippet source | anchor +snippet source | anchor In a sample class called [TodoEndpoints](https://github.com/JasperFx/wolverine/blob/main/src/Samples/TodoWebService/TodoWebService/Endpoints.cs) @@ -159,7 +159,7 @@ will shine in more complicated endpoints. Consider this endpoint just to return the data for a single `Todo` document: - + ```cs // Wolverine can infer the 200/404 status codes for you here // so there's no code noise just to satisfy OpenAPI tooling @@ -167,7 +167,7 @@ Consider this endpoint just to return the data for a single `Todo` document: public static Task GetTodo(int id, IQuerySession session, CancellationToken cancellation) => session.LoadAsync(id, cancellation); ``` -snippet source | anchor +snippet source | anchor At this point it's effectively de rigueur for any web service to support [OpenAPI](https://www.openapis.org/) documentation directly @@ -212,7 +212,7 @@ handling and response by whether or not the document actually exists. Just to sh and also how WolverineFx.Http supports middleware, consider this more complex endpoint: - + ```cs public static class UpdateTodoEndpoint { @@ -233,7 +233,7 @@ public static class UpdateTodoEndpoint } } ``` -snippet source | anchor +snippet source | anchor ## How it Works diff --git a/docs/guide/http/marten.md b/docs/guide/http/marten.md index 574f5a1bf..7f639746a 100644 --- a/docs/guide/http/marten.md +++ b/docs/guide/http/marten.md @@ -84,7 +84,7 @@ If you want soft-deleted documents to be treated as `NULL` for a endpoint, you c In combination with `Required = true` that means the endpoint will return 404 for missing and soft-deleted documents. - + ```cs [WolverineGet("/invoices/soft-delete/{id}")] public static Invoice GetSoftDeleted([Document(Required = true, MaybeSoftDeleted = false)] Invoice invoice) @@ -92,7 +92,7 @@ public static Invoice GetSoftDeleted([Document(Required = true, MaybeSoftDeleted return invoice; } ``` -snippet source | anchor +snippet source | anchor @@ -242,7 +242,7 @@ public class Order To append a single event to an event stream from an HTTP endpoint, you can use a return value like so: - + ```cs [AggregateHandler] [WolverinePost("/orders/ship"), EmptyResponse] @@ -255,7 +255,7 @@ public static OrderShipped Ship(ShipOrder command, Order order) return new OrderShipped(); } ``` -snippet source | anchor +snippet source | anchor Or potentially append multiple events using the `Events` type as a return value like this sample: @@ -330,7 +330,14 @@ projections. If you want to inject the current state of an event sourced aggregate as a parameter into an HTTP endpoint method, use the `[ReadAggregate]` attribute like this: -snippet: sample_using_ReadAggregate_in_HTTP + + +```cs +[WolverineGet("/orders/latest/{id}")] +public static Order GetLatest(Guid id, [ReadAggregate] Order order) => order; +``` +snippet source | anchor + If the aggregate doesn't exist, the HTTP request will stop with a 404 status code. The aggregate/stream identity is found with the same rules as the `[Entity]` or `[Aggregate]` attributes: diff --git a/docs/guide/http/messaging.md b/docs/guide/http/messaging.md index d0f499ffd..318a84596 100644 --- a/docs/guide/http/messaging.md +++ b/docs/guide/http/messaging.md @@ -10,7 +10,7 @@ So there's absolutely nothing stopping you from just using `IMessageBus` as an i dependency to a Wolverine HTTP endpoint method to publish messages like this sample: - + ```cs // This would have an empty response and a 204 status code [WolverinePost("/spawn3")] @@ -20,7 +20,7 @@ public static async ValueTask SendViaMessageBus(IMessageBus bus) await bus.PublishAsync(new HttpMessage2("bar")); } ``` -snippet source | anchor +snippet source | anchor But of course there's some other alternatives to directly using `IMessageBus` by utilizing Wolverine's [cascading messages](/guide/handlers/cascading) @@ -39,7 +39,7 @@ to be immediately published to Wolverine without any need for additional Wolveri Note that this mechanism will return an empty body with a status code of 202 to denote future processing. - + ```cs var builder = WebApplication.CreateBuilder(); @@ -61,13 +61,13 @@ app.MapWolverineEndpoints(opts => // and the rest of your application configuration and bootstrapping ``` -snippet source | anchor +snippet source | anchor On the other hand, the `PublishAsync()` method will send a message if there is a known subscriber and ignore the message if there is no subscriber (as explained in [sending or publishing Messages](/guide/messaging/message-bus#sending-or-publishing-messages)): - + ```cs var builder = WebApplication.CreateBuilder(); @@ -89,7 +89,7 @@ app.MapWolverineEndpoints(opts => // and the rest of your application configuration and bootstrapping ``` -snippet source | anchor +snippet source | anchor Middleware policies from Wolverine.Http are applicable to these endpoints, so for example, it's feasible to use @@ -104,7 +104,7 @@ that this collection of objects is meant to be cascaded messages that are publis Here's an example: - + ```cs // This would have a string response and a 200 status code [WolverinePost("/spawn")] @@ -121,14 +121,14 @@ public static (string, OutgoingMessages) Post(SpawnInput input) return ("got it", messages); } ``` -snippet source | anchor +snippet source | anchor Otherwise, if you want to make it clearer from the signature of your HTTP handler method what messages are cascaded and there's no variance in the type of messages published, you can use additional tuple return values like this: - + ```cs // This would have an empty response and a 204 status code [EmptyResponse] @@ -138,5 +138,5 @@ public static (HttpMessage1, HttpMessage2) Post() return new(new HttpMessage1("foo"), new HttpMessage2("bar")); } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/http/metadata.md b/docs/guide/http/metadata.md index d6534b362..e1c355392 100644 --- a/docs/guide/http/metadata.md +++ b/docs/guide/http/metadata.md @@ -68,7 +68,7 @@ for finer grained control. Here's a sample from the Wolverine testing code that determine the OpenAPI operation id: - + ```cs // This class is NOT distributed in any kind of Nuget today, but feel very free // to copy this code into your own as it is at least tested through Wolverine's @@ -84,7 +84,7 @@ public class WolverineOperationFilter : IOperationFilter // IOperationFilter is } } ``` -snippet source | anchor +snippet source | anchor And that would be registered with Swashbuckle inside of your `Program.Main()` method like so: @@ -135,7 +135,7 @@ an HTTP endpoint, you can have your response type implement the `IHttpAware` int consider the `CreationResponse` type in Wolverine: - + ```cs /// /// Base class for resource types that denote some kind of resource being created @@ -161,7 +161,7 @@ public record CreationResponse([StringSyntax("Route")]string Url) : IHttpAware public static CreationResponse For(T value, string url) => new CreationResponse(url, value); } ``` -snippet source | anchor +snippet source | anchor Any endpoint that returns `CreationResponse` or a sub class will automatically expose a status code of `201` for successful diff --git a/docs/guide/http/middleware.md b/docs/guide/http/middleware.md index 991b13a1f..1b6700a9c 100644 --- a/docs/guide/http/middleware.md +++ b/docs/guide/http/middleware.md @@ -59,7 +59,7 @@ object is anything else, Wolverine will execute that `IResult` and stop processi For a little more complex example, here's part of the Fluent Validation middleware for Wolverine.Http: - + ```cs [MethodImpl(MethodImplOptions.AggressiveInlining)] public static async Task ExecuteOne(IValidator validator, IProblemDetailSource source, T message) @@ -79,7 +79,7 @@ public static async Task ExecuteOne(IValidator validator, IProble return WolverineContinue.Result(); } ``` -snippet source | anchor +snippet source | anchor Likewise, you can also just return a `null` from middleware for `IResult` and Wolverine will interpret that as @@ -161,7 +161,7 @@ this middleware is applied to any endpoint that also uses Wolverine message publ from the `HttpContext` to subsequent Wolverine messages published during the request: - + ```cs public static class RequestIdMiddleware { @@ -177,13 +177,13 @@ public static class RequestIdMiddleware } } ``` -snippet source | anchor +snippet source | anchor And a matching `IHttpPolicy` to apply that middleware to any HTTP endpoint where there is a dependency on Wolverine's `IMessageContext` or `IMessageBus`: - + ```cs internal class RequestIdPolicy : IHttpPolicy { @@ -201,7 +201,7 @@ internal class RequestIdPolicy : IHttpPolicy } } ``` -snippet source | anchor +snippet source | anchor Lastly, this particular policy is included by default, but if it wasn't, this is the code to apply it explicitly: diff --git a/docs/guide/http/multi-tenancy.md b/docs/guide/http/multi-tenancy.md index 00a89ad86..7068dd74e 100644 --- a/docs/guide/http/multi-tenancy.md +++ b/docs/guide/http/multi-tenancy.md @@ -260,7 +260,7 @@ of multi-tenancy, so you can completely opt out of all tenant id detection and a `[NotTenanted]` attribute as shown here in the tests: - + ```cs // Mark this endpoint as not using any kind of multi-tenancy [WolverineGet("/nottenanted"), NotTenanted] @@ -269,7 +269,7 @@ public static string NoTenantNoProblem() return "hey"; } ``` -snippet source | anchor +snippet source | anchor If the above usage completely disabled all tenant id detection or validation, in the case of an endpoint that *might* be @@ -299,7 +299,7 @@ parameters. Wolverine still has you covered by allowing you to create custom imp interface: - + ```cs /// /// Used to create new strategies to detect the tenant id from an HttpContext @@ -315,13 +315,13 @@ public interface ITenantDetection public ValueTask DetectTenant(HttpContext context); } ``` -snippet source | anchor +snippet source | anchor As any example, the route argument detection implementation looks like this: - + ```cs internal class ArgumentDetection : ITenantDetection, ISynchronousTenantDetection { @@ -350,7 +350,7 @@ internal class ArgumentDetection : ITenantDetection, ISynchronousTenantDetection } } ``` -snippet source | anchor +snippet source | anchor ::: tip @@ -462,7 +462,7 @@ Using the `WolverineFx.Http.Marten` Nuget, there's a helper to replace Marten's with a multi-tenanted version like this: - + ```cs builder.Services.AddMartenTenancyDetection(tenantId => { @@ -470,8 +470,8 @@ builder.Services.AddMartenTenancyDetection(tenantId => tenantId.DefaultIs("default-tenant"); }); ``` -snippet source | anchor - +snippet source | anchor + ```cs builder.Services.AddMartenTenancyDetection(tenantId => { @@ -482,6 +482,6 @@ builder.Services.AddMartenTenancyDetection(tenantId => session.CorrelationId = c.TraceIdentifier; }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/http/policies.md b/docs/guide/http/policies.md index c7405ae65..01840657e 100644 --- a/docs/guide/http/policies.md +++ b/docs/guide/http/policies.md @@ -4,7 +4,7 @@ Custom policies can be created for HTTP endpoints through either creating your o shown below: - + ```cs /// /// Use to apply your own conventions or policies to HTTP endpoint handlers @@ -20,7 +20,7 @@ public interface IHttpPolicy void Apply(IReadOnlyList chains, GenerationRules rules, IServiceContainer container); } ``` -snippet source | anchor +snippet source | anchor And then adding a policy to the `WolverineHttpOptions` like this code from the Fluent Validation extension for HTTP: @@ -105,7 +105,7 @@ app.MapWolverineEndpoints(opts => Wolverine has an additional type of policy that deals with how an endpoints primary result is handled. - + ```cs /// /// Use to apply custom handling to the primary result of an HTTP endpoint handler @@ -120,7 +120,7 @@ public interface IResourceWriterPolicy bool TryApply(HttpChain chain); } ``` -snippet source | anchor +snippet source | anchor Only one of these so called resource writer policies can apply to each endpoint and there are a couple of built in policies already. diff --git a/docs/guide/http/problemdetails.md b/docs/guide/http/problemdetails.md index b839abe13..29fe6a8e4 100644 --- a/docs/guide/http/problemdetails.md +++ b/docs/guide/http/problemdetails.md @@ -7,7 +7,7 @@ off, explicit validation for certain endpoints. Consider this contrived sample endpoint with explicit validation being done in a "Before" middleware method: - + ```cs public class ProblemDetailsUsageEndpoint { @@ -35,7 +35,7 @@ public class ProblemDetailsUsageEndpoint public record NumberMessage(int Number); ``` -snippet source | anchor +snippet source | anchor Wolverine.Http now (as of 1.2.0) has a convention that sees a return value of `ProblemDetails` and looks at that as a diff --git a/docs/guide/http/security.md b/docs/guide/http/security.md index be0fd01dd..849bced11 100644 --- a/docs/guide/http/security.md +++ b/docs/guide/http/security.md @@ -16,7 +16,7 @@ app.MapWolverineEndpoints(opts => or more selectively, the code above is just syntactical sugar for: - + ```cs /// /// Equivalent of calling RequireAuthorization() on all wolverine endpoints @@ -26,5 +26,5 @@ public void RequireAuthorizeOnAll() ConfigureEndpoints(e => e.RequireAuthorization()); } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/logging.md b/docs/guide/logging.md index 6d5688c57..3127570c0 100644 --- a/docs/guide/logging.md +++ b/docs/guide/logging.md @@ -43,7 +43,7 @@ you don't want logging for that particular message type, but do for all other me level for only that specific message type like so: - + ```cs public class CustomizedHandler { @@ -64,7 +64,7 @@ public class CustomizedHandler } } ``` -snippet source | anchor +snippet source | anchor Methods on message handler types with the signature: @@ -84,7 +84,7 @@ way to do that is to use the `[WolverineLogging]` attribute on either the handle below: - + ```cs public class QuietMessage; @@ -100,7 +100,7 @@ public class QuietMessageHandler } } ``` -snippet source | anchor +snippet source | anchor @@ -352,7 +352,7 @@ public const string SendingResumed = "wolverine.sending.resumed"; /// public const string TooManySenderFailures = "TooManySenderFailures"; ``` -snippet source | anchor +snippet source | anchor ## Message Correlation diff --git a/docs/guide/messages.md b/docs/guide/messages.md index c9f72ad29..c97fd4a6b 100644 --- a/docs/guide/messages.md +++ b/docs/guide/messages.md @@ -19,7 +19,7 @@ to Newtonsoft.JSON or to use higher performance [MemoryPack](/guide/messages.htm Let's say that you have a basic message structure like this: - + ```cs public class PersonBorn { @@ -33,7 +33,7 @@ public class PersonBorn public int Year { get; set; } } ``` -snippet source | anchor +snippet source | anchor By default, Wolverine will identify this type by just using the .NET full name like so: @@ -114,12 +114,12 @@ The marker types shown above may be helpful in transitioning an existing codebas You can optionally use an attribute to mark a type as a message: - + ```cs [WolverineMessage] public record CloseIssue(Guid Id); ``` -snippet source | anchor +snippet source | anchor Or lastly, make up your own criteria to find and mark message types within your system as shown below: @@ -143,7 +143,7 @@ Going back to the original `PersonBorn` message class in previous sections, let' create a new version of that message that is no longer structurally equivalent to the original message: - + ```cs [MessageIdentity("person-born", Version = 2)] public class PersonBornV2 @@ -153,7 +153,7 @@ public class PersonBornV2 public DateTime Birthday { get; set; } } ``` -snippet source | anchor +snippet source | anchor The `[Version("V2")]` attribute usage tells Wolverine that this class is "V2" for the `message-type` = "person-born." @@ -176,14 +176,14 @@ And to instead opt into using System.Text.Json with different defaults -- which increased risk of serialization failures -- use this syntax where `opts` is a `WolverineOptions` object: - + ```cs opts.UseSystemTextJsonForSerialization(stj => { stj.UnknownTypeHandling = JsonUnknownTypeHandling.JsonNode; }); ``` -snippet source | anchor +snippet source | anchor When using Newtonsoft.Json, the default configuration is: @@ -203,7 +203,7 @@ return new JsonSerializerSettings To customize the Newtonsoft.Json serialization, use this option: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -214,7 +214,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ### MessagePack Serialization @@ -305,7 +305,7 @@ using var host = await Host.CreateDefaultBuilder() If you make breaking changes to an incoming message in a later version, you can simply handle both versions of that message separately: - + ```cs public class PersonCreatedHandler { @@ -320,14 +320,14 @@ public class PersonCreatedHandler } } ``` -snippet source | anchor +snippet source | anchor Or you could use a custom `IMessageDeserializer` to read incoming messages from V1 into the new V2 message type, or you can take advantage of message forwarding so you only need to handle one message type using the `IForwardsTo` interface as shown below: - + ```cs public class PersonBorn : IForwardsTo { @@ -348,13 +348,13 @@ public class PersonBorn : IForwardsTo } } ``` -snippet source | anchor +snippet source | anchor Which forwards to the current message type: - + ```cs [MessageIdentity("person-born", Version = 2)] public class PersonBornV2 @@ -364,7 +364,7 @@ public class PersonBornV2 public DateTime Birthday { get; set; } } ``` -snippet source | anchor +snippet source | anchor Using this strategy, other systems could still send your system the original `application/vnd.person-born.v1+json` formatted diff --git a/docs/guide/messaging/expiration.md b/docs/guide/messaging/expiration.md index 4675d516e..ca232362b 100644 --- a/docs/guide/messaging/expiration.md +++ b/docs/guide/messaging/expiration.md @@ -20,7 +20,7 @@ public DateTimeOffset? DeliverBy set => _deliverBy = value?.ToUniversalTime(); } ``` -snippet source | anchor +snippet source | anchor At runtime, Wolverine will: diff --git a/docs/guide/messaging/listeners.md b/docs/guide/messaging/listeners.md index 92d90a8de..5335674e2 100644 --- a/docs/guide/messaging/listeners.md +++ b/docs/guide/messaging/listeners.md @@ -139,7 +139,7 @@ In the case where you need messages from a single endpoint to be processed in st you have the `ListenWithStrictOrdering()` option: - + ```cs var host = await Host.CreateDefaultBuilder().UseWolverine(opts => { @@ -153,7 +153,7 @@ var host = await Host.CreateDefaultBuilder().UseWolverine(opts => .ListenWithStrictOrdering(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor This option does a couple things: diff --git a/docs/guide/messaging/message-bus.md b/docs/guide/messaging/message-bus.md index 7a745774c..1cf4d1798 100644 --- a/docs/guide/messaging/message-bus.md +++ b/docs/guide/messaging/message-bus.md @@ -141,7 +141,7 @@ You can explicitly override this behavior on a handler by handler basis with the as shown below: - + ```cs public class CreateItemCommandHandler { @@ -162,7 +162,7 @@ public class CreateItemCommandHandler } } ``` -snippet source | anchor +snippet source | anchor ## Disabling Remote Request/Reply @@ -185,7 +185,7 @@ using var host = Host.CreateDefaultBuilder() opts.EnableRemoteInvocation = false; }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Sending or Publishing Messages @@ -279,7 +279,7 @@ actually have a couple different syntactical options. First, if you're directly can schedule a message with a delay using this extension method: - + ```cs public async Task schedule_send(IMessageContext context, Guid issueId) { @@ -298,13 +298,13 @@ public async Task schedule_send(IMessageContext context, Guid issueId) }); } ``` -snippet source | anchor +snippet source | anchor Or using an absolute time, with this overload of the extension method: - + ```cs public async Task schedule_send_at_5_tomorrow_afternoon(IMessageContext context, Guid issueId) { @@ -321,7 +321,7 @@ public async Task schedule_send_at_5_tomorrow_afternoon(IMessageContext context, await context.ScheduleAsync(timeout, time); } ``` -snippet source | anchor +snippet source | anchor Now, Wolverine tries really hard to enable you to use [pure functions](https://en.wikipedia.org/wiki/Pure_function) for as many message handlers as possible, so @@ -354,13 +354,13 @@ Lastly, there's a special base class called `TimeoutMessage` that your message t directly to the message itself for easy usage as a cascaded message. Here's an example message type: - + ```cs // This message will always be scheduled to be delivered after // a one minute delay public record OrderTimeout(string Id) : TimeoutMessage(1.Minutes()); ``` -snippet source | anchor +snippet source | anchor Which is used within this sample saga implementation: @@ -387,7 +387,7 @@ public static (Order, OrderTimeout) Start(StartOrder order, ILogger logge TODO -- more text here. NEW PAGE??? - + ```cs public static async Task SendMessagesWithDeliveryOptions(IMessageBus bus) { @@ -405,7 +405,7 @@ public static async Task SendMessagesWithDeliveryOptions(IMessageBus bus) .WithHeader("tenant", "one")); } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/subscriptions.md b/docs/guide/messaging/subscriptions.md index 7614466fa..b08c6108e 100644 --- a/docs/guide/messaging/subscriptions.md +++ b/docs/guide/messaging/subscriptions.md @@ -44,7 +44,7 @@ of the Wolverine configuration. Programmatically, this code shows how to "look" into the configured Wolverine subscriptions for a message type: - + ```cs public static void PreviewRouting(IHost host) { @@ -72,7 +72,7 @@ public static void PreviewRouting(IHost host) } } ``` -snippet source | anchor +snippet source | anchor First, you can always use the [command line support](/guide/command-line) to preview Wolverine's known message types by using: @@ -95,7 +95,7 @@ preview functionality by "telling" Wolverine what your outgoing message types ar To route messages to specific endpoints, we can apply static message routing rules by using a routing rule as shown below: - + ```cs using var host = Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -144,7 +144,7 @@ using var host = Host.CreateDefaultBuilder() opts.PublishAllMessages().ToPort(3333); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor Do note that doing the message type filtering by namespace will also include child namespaces. In @@ -215,7 +215,7 @@ for a message type that "knows" how to create the Wolverine `Envelope` for a sin outgoing message to a single subscribing endpoint: - + ```cs /// /// Contains all the rules for where and how an outgoing message @@ -227,7 +227,7 @@ public interface IMessageRoute WolverineRuntime runtime, string? topicName); } ``` -snippet source | anchor +snippet source | anchor This type "knows" about any endpoint or model sending customizations like delivery expiration @@ -291,7 +291,7 @@ Let's say you want to use a completely different conventional routing topology t of the box. You can do that by creating your own implementation of this interface: - + ```cs /// /// Plugin for doing any kind of conventional message routing @@ -314,7 +314,7 @@ public interface IMessageRoutingConvention IEnumerable DiscoverSenders(Type messageType, IWolverineRuntime runtime); } ``` -snippet source | anchor +snippet source | anchor As a concrete example, the Wolverine team received [this request](https://github.com/JasperFx/wolverine/issues/1130) to conventionally route messages based on @@ -323,7 +323,7 @@ That's not something that Wolverine supports out of the box, but you could build convention like this: - + ```cs public class RouteKeyConvention : IMessageRoutingConvention { @@ -357,13 +357,13 @@ public class RouteKeyConvention : IMessageRoutingConvention } } ``` -snippet source | anchor +snippet source | anchor And register it to your Wolverine application like so: - + ```cs var builder = Host.CreateApplicationBuilder(); var rabbitConnectionString = builder @@ -384,7 +384,7 @@ builder.UseWolverine(opts => // actually start the app... ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/local.md b/docs/guide/messaging/transports/local.md index b769a8ee3..c7037abc2 100644 --- a/docs/guide/messaging/transports/local.md +++ b/docs/guide/messaging/transports/local.md @@ -31,7 +31,7 @@ Some things to know about the local queues: If you want to enqueue a message locally to a specific worker queue, you can use this syntax: - + ```cs public ValueTask EnqueueToQueue(IMessageContext bus) { @@ -48,7 +48,7 @@ public ValueTask EnqueueToQueue(IMessageContext bus) return bus.EndpointFor("highpriority").SendAsync(@event); } ``` -snippet source | anchor +snippet source | anchor ## Scheduling Local Execution @@ -100,7 +100,7 @@ public class ImportanceMessage; Otherwise, you can take advantage of Wolverine's message routing rules like this: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -111,7 +111,7 @@ using var host = await Host.CreateDefaultBuilder() .ToLocalQueue("important"); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor The routing rules and/or `[LocalQueue]` routing is also honored for cascading messages, meaning that any message that is handled inside a Wolverine system could publish cascading messages to the local worker queues. @@ -219,7 +219,7 @@ the Wolverine naming conventions. To get back to leaning more on the type system interface that can be implemented on any handler type to configure the local queue where that handler would run: - + ```cs /// /// Helps mark a handler to configure the local queue that its messages @@ -231,7 +231,7 @@ public interface IConfigureLocalQueue static abstract void Configure(LocalQueueConfiguration configuration); } ``` -snippet source | anchor +snippet source | anchor ::: tip @@ -242,7 +242,7 @@ handler type itself cannot be static. Just a .NET quirk. To use this, just implement that interface on any message handler type: - + ```cs public class MultipleMessage1Handler : IConfigureLocalQueue { @@ -259,7 +259,7 @@ public class MultipleMessage1Handler : IConfigureLocalQueue } } ``` -snippet source | anchor +snippet source | anchor ## Durable Local Messages @@ -269,7 +269,7 @@ The local worker queues can optionally be designated as "durable," meaning that Here is an example of configuring a local queue to be durable: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -282,7 +282,7 @@ using var host = await Host.CreateDefaultBuilder() .UseDurableInbox(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor @@ -294,7 +294,7 @@ See [Durable Inbox and Outbox Messaging](/guide/durability/) for more informatio The queues are built on top of the TPL Dataflow library, so it's pretty easy to configure parallelization (how many concurrent messages could be handled by a queue). Here's an example of how to establish this: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -328,7 +328,7 @@ using var host = await Host.CreateDefaultBuilder() opts.LocalQueue("four").UseDurableInbox(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor @@ -342,7 +342,7 @@ locally enqueued messages or scheduled messages that may have initially failed. In the sample Wolverine configuration shown below: - + ```cs using var host = await Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -353,7 +353,7 @@ using var host = await Host.CreateDefaultBuilder() .ToLocalQueue("important"); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor Calling `IMessageBus.SendAsync(new Message2())` would publish the message to the local "important" queue. diff --git a/docs/guide/messaging/transports/mqtt.md b/docs/guide/messaging/transports/mqtt.md index d15b9d4bf..6f41679d3 100644 --- a/docs/guide/messaging/transports/mqtt.md +++ b/docs/guide/messaging/transports/mqtt.md @@ -127,21 +127,12 @@ would be derived from either Wolverine's [message type name](/guide/messages.htm or by using the `[Topic("topic name")]` attribute as shown below: - + ```cs [Topic("one")] public class TopicMessage1; ``` -snippet source | anchor - -```cs -[Topic("color.blue")] -public class FirstMessage -{ - public Guid Id { get; set; } = Guid.NewGuid(); -} -``` -snippet source | anchor +snippet source | anchor ## Publishing by Topic Rules @@ -263,7 +254,7 @@ For more complex interoperability, you can implement the `IMqttEnvelopeMapper` i incoming and outgoing MQTT messages and the Wolverine `Envelope` structure. Here's an example: - + ```cs public class MyMqttEnvelopeMapper : IMqttEnvelopeMapper { @@ -294,7 +285,7 @@ public class MyMqttEnvelopeMapper : IMqttEnvelopeMapper } } ``` -snippet source | anchor +snippet source | anchor And apply that to an MQTT topic like so: diff --git a/docs/guide/messaging/transports/rabbitmq/conventional-routing.md b/docs/guide/messaging/transports/rabbitmq/conventional-routing.md index 769a58dd0..773063b89 100644 --- a/docs/guide/messaging/transports/rabbitmq/conventional-routing.md +++ b/docs/guide/messaging/transports/rabbitmq/conventional-routing.md @@ -20,7 +20,7 @@ using var host = await Host.CreateDefaultBuilder() .UseConventionalRouting(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor With the defaults from above, for each message that the application can handle @@ -73,7 +73,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Adjusting Routing Conventions @@ -122,7 +122,7 @@ var receiver = WolverineHost.For(opts => }); }); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/deadletterqueues.md b/docs/guide/messaging/transports/rabbitmq/deadletterqueues.md index e45bcf6b6..4b6fc6488 100644 --- a/docs/guide/messaging/transports/rabbitmq/deadletterqueues.md +++ b/docs/guide/messaging/transports/rabbitmq/deadletterqueues.md @@ -31,7 +31,7 @@ using var host = await Host.CreateDefaultBuilder() .DeadLetterQueueing(new DeadLetterQueue("incoming-errors")); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ::: warning @@ -66,7 +66,7 @@ using var host = await Host.CreateDefaultBuilder() .DeadLetterQueueing(new DeadLetterQueue("incoming-errors", DeadLetterQueueMode.InteropFriendly)); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor And lastly, if you don't particularly want to have any Rabbit MQ dead letter queues and you quite like the [database backed @@ -93,7 +93,7 @@ using var host = await Host.CreateDefaultBuilder() opts.ListenToRabbitQueue("incoming").DisableDeadLetterQueueing(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/index.md b/docs/guide/messaging/transports/rabbitmq/index.md index 4879bf902..18278c777 100644 --- a/docs/guide/messaging/transports/rabbitmq/index.md +++ b/docs/guide/messaging/transports/rabbitmq/index.md @@ -79,7 +79,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor To only send Rabbit MQ messages, but never receive them: @@ -108,7 +108,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor @@ -134,7 +134,7 @@ using var host = await Host.CreateDefaultBuilder() .EnableWolverineControlQueues(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor @@ -170,7 +170,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor Of course, doing so means that you will not be able to do request/reply through Rabbit MQ with your Wolverine application. diff --git a/docs/guide/messaging/transports/rabbitmq/interoperability.md b/docs/guide/messaging/transports/rabbitmq/interoperability.md index 6e0580743..1a16c36cf 100644 --- a/docs/guide/messaging/transports/rabbitmq/interoperability.md +++ b/docs/guide/messaging/transports/rabbitmq/interoperability.md @@ -39,7 +39,7 @@ builder.UseWolverine(opts => using var host = builder.Build(); await host.StartAsync(); ``` -snippet source | anchor +snippet source | anchor With this setting, there is **no other required headers** for Wolverine to process incoming messages. However, Wolverine will be @@ -135,7 +135,7 @@ builder.UseWolverine(opts => using var host = builder.Build(); await host.StartAsync(); ``` -snippet source | anchor +snippet source | anchor @@ -155,7 +155,7 @@ At this point, the interoperability is only built and tested for the [Rabbit MQ Here's a sample: - + ```cs Wolverine = await Host.CreateDefaultBuilder().UseWolverine(opts => { @@ -181,7 +181,7 @@ Wolverine = await Host.CreateDefaultBuilder().UseWolverine(opts => opts.Policies.RegisterInteropMessageAssembly(typeof(IInterfaceMessage).Assembly); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Interoperability with Mass Transit @@ -198,7 +198,7 @@ with MassTransit, and don't try to use that endpoint for Wolverine to Wolverine The configuration to do this is shown below: - + ```cs Wolverine = await Host.CreateDefaultBuilder().UseWolverine(opts => { @@ -226,5 +226,5 @@ Wolverine = await Host.CreateDefaultBuilder().UseWolverine(opts => .DefaultIncomingMessage().UseForReplies(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/listening.md b/docs/guide/messaging/transports/rabbitmq/listening.md index 5d2636a9f..560ed08a9 100644 --- a/docs/guide/messaging/transports/rabbitmq/listening.md +++ b/docs/guide/messaging/transports/rabbitmq/listening.md @@ -38,7 +38,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor To optimize and tune the message processing, you may want to read more about the [Rabbit MQ prefetch count and prefetch @@ -80,5 +80,5 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/multiple-brokers.md b/docs/guide/messaging/transports/rabbitmq/multiple-brokers.md index e589155d7..1a4562d62 100644 --- a/docs/guide/messaging/transports/rabbitmq/multiple-brokers.md +++ b/docs/guide/messaging/transports/rabbitmq/multiple-brokers.md @@ -39,7 +39,7 @@ builder.UseWolverine(opts => opts.PublishAllMessages().ToRabbitTopicsOnNamedBroker(external, "topics"); }); ``` -snippet source | anchor +snippet source | anchor The `Uri` values for endpoints to the additional broker follows the same rules as the normal usage of the Rabbit MQ diff --git a/docs/guide/messaging/transports/rabbitmq/object-management.md b/docs/guide/messaging/transports/rabbitmq/object-management.md index 162917b2c..2a3157cd9 100644 --- a/docs/guide/messaging/transports/rabbitmq/object-management.md +++ b/docs/guide/messaging/transports/rabbitmq/object-management.md @@ -30,7 +30,7 @@ using var host = await Host.CreateDefaultBuilder() opts.PublishAllMessages().ToRabbitExchange("exchange1"); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor At development time -- or occasionally in production systems -- you may want to have the messaging @@ -47,7 +47,7 @@ using var host = await Host.CreateDefaultBuilder() .AutoPurgeOnStartup(); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor Or you can be more selective and only have certain queues of volatile messages purged @@ -64,7 +64,7 @@ using var host = await Host.CreateDefaultBuilder() .DeclareQueue("queue2", q => q.PurgeOnStartup = true); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor Wolverine's Rabbit MQ integration also supports the [Oakton stateful resource](https://jasperfx.github.io/oakton/guide/host/resources.html) model, @@ -172,7 +172,36 @@ are largely not impacted otherwise. Here are your options for configuring one or many queues as opting into being a "Quorum Queue" or a "Stream": -snippet: sample_configuring_quorum_or_streams_in_rabbit_MQ + + +```cs +var builder = Host.CreateApplicationBuilder(); +builder.UseWolverine(opts => +{ + opts + .UseRabbitMq(builder.Configuration.GetConnectionString("rabbit")) + + // You can configure the queue type for declaration with this + // usage as well + .DeclareQueue("stream", q => q.QueueType = QueueType.stream) + + // Use quorum queues by default as a policy + .UseQuorumQueues() + + // Or instead use streams + .UseStreamsAsQueues(); + + opts.ListenToRabbitQueue("quorum1") + // Override the queue type in declarations for a + // single queue, and the explicit configuration will win + // out over any policy or convention + .QueueType(QueueType.quorum); + + +}); +``` +snippet source | anchor + There are just a few things to know: @@ -186,7 +215,7 @@ you can quickly access and make additions to the Rabbit MQ integration with your like so: - + ```cs public class MyModuleExtension : IWolverineExtension { @@ -201,6 +230,6 @@ public class MyModuleExtension : IWolverineExtension } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/publishing.md b/docs/guide/messaging/transports/rabbitmq/publishing.md index c73fc2f67..b6352655b 100644 --- a/docs/guide/messaging/transports/rabbitmq/publishing.md +++ b/docs/guide/messaging/transports/rabbitmq/publishing.md @@ -23,7 +23,7 @@ using var host = await Host.CreateDefaultBuilder() opts.PublishAllMessages().ToRabbitQueue("special", queue => { queue.IsExclusive = true; }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Publish to an Exchange @@ -55,7 +55,7 @@ using var host = await Host.CreateDefaultBuilder() }); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor ## Publish to a Routing Key @@ -84,6 +84,6 @@ using var host = await Host.CreateDefaultBuilder() opts.PublishAllMessages().ToRabbitExchange("exchange1"); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/rabbitmq/topics.md b/docs/guide/messaging/transports/rabbitmq/topics.md index a5fd63d45..8b1904d0e 100644 --- a/docs/guide/messaging/transports/rabbitmq/topics.md +++ b/docs/guide/messaging/transports/rabbitmq/topics.md @@ -23,7 +23,7 @@ using var host = await Host.CreateDefaultBuilder() opts.ListenToRabbitQueue(""); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor While we're specifying the exchange name ("topics-exchange"), we did nothing to specify the topic @@ -35,7 +35,7 @@ name. With this set up, when you publish a message in this application like so: var publisher = host.MessageBus(); await publisher.SendAsync(new Message1()); ``` -snippet source | anchor +snippet source | anchor You will be sending that message to the "topics-exchange" with a topic name derived from @@ -48,19 +48,13 @@ on a message type like so: ```cs -[Topic("one")] -public class TopicMessage1; -``` -snippet source | anchor - -```cs [Topic("color.blue")] public class FirstMessage { public Guid Id { get; set; } = Guid.NewGuid(); } ``` -snippet source | anchor +snippet source | anchor Of course, you can always explicitly send a message to a specific topic with this syntax: @@ -70,7 +64,7 @@ Of course, you can always explicitly send a message to a specific topic with thi ```cs await publisher.BroadcastToTopicAsync("color.*", new Message1()); ``` -snippet source | anchor +snippet source | anchor Note two things about the code above: @@ -117,7 +111,7 @@ public interface ITenantMessage string TenantId { get; } } ``` -snippet source | anchor +snippet source | anchor Let's say that any message that implements that interface, we want published to the @@ -144,5 +138,5 @@ builder.UseWolverine(opts => using var host = builder.Build(); await host.StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/messaging/transports/tcp.md b/docs/guide/messaging/transports/tcp.md index 6ce1f7dbc..56a3df8c3 100644 --- a/docs/guide/messaging/transports/tcp.md +++ b/docs/guide/messaging/transports/tcp.md @@ -13,7 +13,7 @@ You can listen to messages from as many ports as you like, but be aware of port To listen for messages with the TCP transport, use the `ListenAtPort()` extension method shown below: - + ```cs public static IHost CreateHostBuilder() { @@ -41,7 +41,7 @@ public static IHost CreateHostBuilder() return builder.Build(); } ``` -snippet source | anchor +snippet source | anchor Likewise, to publish via TCP, use the `ToPort()` extension method to publish to another port on the same @@ -79,7 +79,7 @@ var answer = bus.EndpointFor("One") or use `ToServerAndPort()` to send messages to a port on another machine: - + ```cs using var host = Host.CreateDefaultBuilder() .UseWolverine(opts => @@ -128,7 +128,7 @@ using var host = Host.CreateDefaultBuilder() opts.PublishAllMessages().ToPort(3333); }).StartAsync(); ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 293e563f3..9175dffdc 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -134,7 +134,7 @@ You can selectively override this behavior and tell Wolverine to publish the res by using the new 3.0 `[AlwaysPublishResponse]` attribute like this: - + ```cs public class CreateItemCommandHandler { @@ -155,5 +155,5 @@ public class CreateItemCommandHandler } } ``` -snippet source | anchor +snippet source | anchor diff --git a/docs/guide/runtime.md b/docs/guide/runtime.md index 4b2771323..82f3156ce 100644 --- a/docs/guide/runtime.md +++ b/docs/guide/runtime.md @@ -8,7 +8,7 @@ everything is just a message. The two key parts of a Wolverine application are messages: - + ```cs // A "command" message public record DebitAccount(long AccountId, decimal Amount); @@ -16,13 +16,13 @@ public record DebitAccount(long AccountId, decimal Amount); // An "event" message public record AccountOverdrawn(long AccountId); ``` -snippet source | anchor +snippet source | anchor And the message handling code for the messages, which in Wolverine's case just means a function or method that accepts the message type as its first argument like so: - + ```cs public static class DebitAccountHandler { @@ -32,7 +32,7 @@ public static class DebitAccountHandler } } ``` -snippet source | anchor +snippet source | anchor ## Invoking a Message Inline @@ -239,7 +239,7 @@ The stateful, running "agents" are exposed through an `IAgent` interface like so: - + ```cs /// /// Models a constantly running background process within a Wolverine @@ -262,13 +262,13 @@ public enum AgentStatus Paused } ``` -snippet source | anchor +snippet source | anchor With related groups of agents built and assigned by IoC-registered implementations of this interface: - + ```cs /// /// Pluggable model for managing the assignment and execution of stateful, "sticky" @@ -310,7 +310,7 @@ public interface IAgentFamily ValueTask EvaluateAssignmentsAsync(AssignmentGrid assignments); } ``` -snippet source | anchor +snippet source | anchor Built in examples of the agent and agent family are: diff --git a/docs/guide/testing.md b/docs/guide/testing.md index 8d16b4491..ef22f8077 100644 --- a/docs/guide/testing.md +++ b/docs/guide/testing.md @@ -19,7 +19,7 @@ For an example, let's look at this message handler for applying a debit to a ban will use [cascading messages](/guide/handlers/cascading) to raise a variable number of additional messages: - + ```cs [Transactional] public static IEnumerable Handle( @@ -51,7 +51,7 @@ public static IEnumerable Handle( yield return new AccountUpdated(account.Id, account.Balance); } ``` -snippet source | anchor +snippet source | anchor The testing extensions can be seen in action by the following test: @@ -118,7 +118,7 @@ Here's a different version of the message handler from the previous section, but directly: - + ```cs [Transactional] public static async Task Handle( @@ -153,7 +153,7 @@ public static async Task Handle( new DeliveryOptions { DeliverWithin = 5.Seconds() }); } ``` -snippet source | anchor +snippet source | anchor To test this handler, we can use `TestMessageContext` as a stand in to just record diff --git a/docs/tutorials/cqrs-with-marten.md b/docs/tutorials/cqrs-with-marten.md index a78f310d3..63c047296 100644 --- a/docs/tutorials/cqrs-with-marten.md +++ b/docs/tutorials/cqrs-with-marten.md @@ -96,7 +96,7 @@ and will probably evolve through subsequent user stories. We're starting from an so we're going to skip ahead to some of our initial event types: - + ```cs public class Incident { @@ -129,7 +129,7 @@ public class Incident public bool ShouldDelete(Archived @event) => true; } ``` -snippet source | anchor +snippet source | anchor ::: info @@ -182,7 +182,7 @@ our code, so here's the first cut at the HTTP endpoint that will log a new incid for the incident in one file: - + ```cs public record LogIncident( Guid CustomerId, @@ -207,7 +207,7 @@ public static class LogIncidentEndpoint } } ``` -snippet source | anchor +snippet source | anchor And maybe there's a few details to unpack. It might help to [see the code](/guide/codegen) that Wolverine generates for this HTTP @@ -338,20 +338,20 @@ overrides so that you are mostly using the application **as it is actually confi As a little tip, I've added this bit of marker code to the very bottom of our `Program` file: - + ```cs // Adding this just makes it easier to bootstrap your // application in a test harness project. Only a convenience public partial class Program{} ``` -snippet source | anchor +snippet source | anchor Having that above, I'll switch to the test harness project and create a shared fixture to bootstrap the `IHost` for the application throughout the integration tests: - + ```cs public class AppFixture : IAsyncLifetime { @@ -384,14 +384,14 @@ public class AppFixture : IAsyncLifetime } } ``` -snippet source | anchor +snippet source | anchor And I like to add a base class for integration tests with some convenience methods that have been useful here and there: - + ```cs [CollectionDefinition("integration")] public class IntegrationCollection : ICollectionFixture; @@ -454,7 +454,7 @@ public abstract class IntegrationContext : IAsyncLifetime } } ``` -snippet source | anchor +snippet source | anchor With all of that in place (and if you're using Docker for your infrastructure, a quick `docker compose up -d` command), @@ -507,7 +507,7 @@ allows you to express most command handlers that target Marten event sourcing as On to the code: - + ```cs public record CategoriseIncident( IncidentCategory Category, @@ -548,7 +548,7 @@ public static class CategoriseIncidentEndpoint } } ``` -snippet source | anchor +snippet source | anchor In this case, I'm sourcing the `Incident` value using the `incidentId` route argument as diff --git a/docs/tutorials/from-mediatr.md b/docs/tutorials/from-mediatr.md index dadefb122..0e595d3dc 100644 --- a/docs/tutorials/from-mediatr.md +++ b/docs/tutorials/from-mediatr.md @@ -159,7 +159,7 @@ avoid the really nasty kind of Exception stack traces you get from many other mi Let's say that you have a Wolverine.HTTP endpoint like so: - + ```cs public record CreateCustomer ( @@ -188,7 +188,7 @@ public static class CreateCustomerEndpoint } } ``` -snippet source | anchor +snippet source | anchor In the application bootstrapping, I've added this option: diff --git a/docs/tutorials/getting-started.md b/docs/tutorials/getting-started.md index 53b5b302c..a39e702d2 100644 --- a/docs/tutorials/getting-started.md +++ b/docs/tutorials/getting-started.md @@ -52,17 +52,17 @@ public record CreateIssue(Guid OriginatorId, string Title, string Description); and - + ```cs public record AssignIssue(Guid IssueId, Guid AssigneeId); ``` -snippet source | anchor +snippet source | anchor Let's jump right into the `Program.cs` file of our new web service: - + ```cs using Oakton; using Quickstart; @@ -104,7 +104,7 @@ app.MapGet("/", () => Results.Redirect("/swagger")); // your Wolverine application return await app.RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor ::: tip @@ -126,7 +126,7 @@ inline. In a simplistic form, here is the entire handler file for the `CreateIss command: - + ```cs namespace Quickstart; @@ -160,7 +160,7 @@ public class CreateIssueHandler } } ``` -snippet source | anchor +snippet source | anchor Hopefully that code is simple enough, but let's talk what you do not see in this code or @@ -194,7 +194,7 @@ the initial web service call. The `IssueHandled` event message will be handled by this code: - + ```cs public static class IssueCreatedHandler { @@ -218,7 +218,7 @@ public static class IssueCreatedHandler } } ``` -snippet source | anchor +snippet source | anchor Now, you'll notice that Wolverine is happy to allow you to use static methods as diff --git a/docs/tutorials/mediator.md b/docs/tutorials/mediator.md index 7f60c6174..bdc9cc7d1 100644 --- a/docs/tutorials/mediator.md +++ b/docs/tutorials/mediator.md @@ -71,7 +71,7 @@ From there, we'll slightly modify the `Program` file generated by the `webapi` t into Wolverine's [extended command line support](/guide/command-line): - + ```cs var builder = WebApplication.CreateBuilder(args); @@ -98,7 +98,7 @@ builder.Services.AddDbContext( // use this DbContext type optionsLifetime: ServiceLifetime.Singleton); ``` -snippet source | anchor +snippet source | anchor Now, let's add a Wolverine message handler that will: @@ -110,7 +110,7 @@ Now, let's add a Wolverine message handler that will: Using idiomatic Wolverine, that handler looks like this: - + ```cs public class ItemHandler { @@ -145,7 +145,7 @@ public class ItemHandler } } ``` -snippet source | anchor +snippet source | anchor **Note**, as long as this handler class is public and in the main application assembly, Wolverine is going @@ -155,11 +155,11 @@ to find it and wire it up inside its execution pipeline. There's no explicit cod Now, moving up to the API layer, we can add a new HTTP endpoint to delegate to Wolverine as a mediator with: - + ```cs app.MapPost("/items/create", (CreateItemCommand cmd, IMessageBus bus) => bus.InvokeAsync(cmd)); ``` -snippet source | anchor +snippet source | anchor There isn't much to this code -- and that's the entire point! When Wolverine registers itself into @@ -184,7 +184,7 @@ As a contrast, here's what the same functionality looks like if you write all th explicitly in a controller action: - + ```cs // This controller does all the transactional work and business // logic all by itself @@ -226,7 +226,7 @@ public class DoItAllMyselfItemController : ControllerBase } } ``` -snippet source | anchor +snippet source | anchor So one, there's just more going on in the `/items/create` HTTP endpoint defined above because you're needing to do a little bit of @@ -246,11 +246,11 @@ output of the message handler to the HTTP response body (and assume we'll use JS example code simpler): - + ```cs app.MapPost("/items/create2", (CreateItemCommand cmd, IMessageBus bus) => bus.InvokeAsync(cmd)); ``` -snippet source | anchor +snippet source | anchor Using the `IMessageBus.Invoke(message)` overload, the returned `ItemCreated` response diff --git a/docs/tutorials/middleware.md b/docs/tutorials/middleware.md index 2a8291ee3..7f5c459e0 100644 --- a/docs/tutorials/middleware.md +++ b/docs/tutorials/middleware.md @@ -32,31 +32,31 @@ Using Wolverine's [conventional middleware approach](/guide/handlers/middleware. command message types that reference an `Account` like so: - + ```cs public interface IAccountCommand { Guid AccountId { get; } } ``` -snippet source | anchor +snippet source | anchor So a command message might look like this: - + ```cs public record CreditAccount(Guid AccountId, decimal Amount) : IAccountCommand; ``` -snippet source | anchor +snippet source | anchor Skipping ahead a little bit, if we had a handler for the `CreditAccount` command type above that was counting on some kind of middleware to just "push" the matching `Account` data in, the handler might just be this: - + ```cs public static class CreditAccountHandler { @@ -78,7 +78,7 @@ public static class CreditAccountHandler } } ``` -snippet source | anchor +snippet source | anchor You'll notice at this point that the message handler is synchronous because it's no longer doing any calls to the database. Besides removing some repetitive code, this appproach @@ -88,7 +88,7 @@ Next, let's build the actual middleware that will attempt to load an `Account` m or be aborted. Here's sample code to do exactly that: - + ```cs // This is *a* way to build middleware in Wolverine by basically just // writing functions/methods. There's a naming convention that @@ -121,7 +121,7 @@ public static class AccountLookupMiddleware } } ``` -snippet source | anchor +snippet source | anchor Now, some notes about the code above: diff --git a/docs/tutorials/ping-pong.md b/docs/tutorials/ping-pong.md index 37e664631..75171fd9e 100644 --- a/docs/tutorials/ping-pong.md +++ b/docs/tutorials/ping-pong.md @@ -7,7 +7,7 @@ To show off some of the messaging, let's just build [a very simple "Ping/Pong" e First off, I'm going to build out a very small shared library just to hold the messages we're going to exchange: - + ```cs public class Ping { @@ -19,13 +19,13 @@ public class Pong public int Number { get; set; } } ``` -snippet source | anchor +snippet source | anchor And next, I'll start a small *Pinger* service with the `dotnet new worker` template. There's just three pieces of code, starting with the boostrapping code: - + ```cs using Messages; using Oakton; @@ -50,13 +50,13 @@ return await Host.CreateDefaultBuilder(args) }) .RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor and the `Worker` class that's just going to publish a new `Ping` message once a second: - + ```cs using Messages; using Wolverine; @@ -88,13 +88,13 @@ public class Worker : BackgroundService } } ``` -snippet source | anchor +snippet source | anchor and lastly a message handler for any `Pong` messages coming back from the `Ponger` we'll build next: - + ```cs using Messages; @@ -108,14 +108,14 @@ public class PongHandler } } ``` -snippet source | anchor +snippet source | anchor Okay then, next let's move on to building the `Ponger` application. This time I'll use `dotnet new console` to start the new project, then add references to our *Messages* library and Wolverine itself. For the bootstrapping, add this code: - + ```cs using Microsoft.Extensions.Hosting; using Oakton; @@ -130,14 +130,14 @@ return await Host.CreateDefaultBuilder(args) }) .RunOaktonCommands(args); ``` -snippet source | anchor +snippet source | anchor And a message handler for the `Ping` messages that will turn right around and shoot a `Pong` response right back to the original sender: - + ```cs using Messages; using Microsoft.Extensions.Logging; @@ -154,8 +154,8 @@ public class PingHandler } } ``` -snippet source | anchor - +snippet source | anchor + ```cs public static class PingHandler { @@ -184,7 +184,7 @@ public static class PingHandler } } ``` -snippet source | anchor +snippet source | anchor If I start up first the *Ponger* service, then the *Pinger* service, I'll see console output like this from *Pinger*: diff --git a/docs/tutorials/railway-programming.md b/docs/tutorials/railway-programming.md index ac4c92f03..c77cc7b9a 100644 --- a/docs/tutorials/railway-programming.md +++ b/docs/tutorials/railway-programming.md @@ -75,7 +75,7 @@ You have more specialized ways of doing that in HTTP endpoints by using the `Pro processing like this example that uses a `Validate()` method to potentially stop processing with a descriptive 400 and error message: - + ```cs public record CategoriseIncident( IncidentCategory Category, @@ -116,7 +116,7 @@ public static class CategoriseIncidentEndpoint } } ``` -snippet source | anchor +snippet source | anchor The value `WolverineContinue.NoProblems` tells Wolverine that everything is good, full speed ahead. Anything else will write the `ProblemDetails`