@@ -141,7 +141,7 @@ public async Task<(User user, UserCreated evt)> HandleAsync(CreateUser cmd)
141141
142142// Usage
143143var user = await mediator .InvokeAsync <User >(new CreateUser (.. .));
144- // UserCreated is auto-published
144+ // UserCreated is auto-published and handlers invoked inline before this method returns
145145```
146146
147147## 📦 Publish API & Behavior
@@ -152,142 +152,6 @@ await mediator.PublishAsync(new OrderShipped(orderId));
152152
153153All handlers run in parallel; if any fail, PublishAsync throws.
154154
155- ### Example: Complete CRUD with Result Types
156-
157- ``` csharp
158- public record GetUserQuery (int Id );
159- public record UpdateUserCommand (int Id , string Name , string Email );
160- public record DeleteUserCommand (int Id );
161-
162- public class UserHandler
163- {
164- public async Task <Result <User >> HandleAsync (GetUserQuery query )
165- {
166- var user = await _repository .GetByIdAsync (query .Id );
167- return user != null
168- ? Result .Ok (user )
169- : Result .NotFound ($" User with ID {query .Id } not found" );
170- }
171-
172- public async Task <Result <User >> HandleAsync (UpdateUserCommand command )
173- {
174- var existingUser = await _repository .GetByIdAsync (command .Id );
175- if (existingUser == null )
176- return Result .NotFound ($" User with ID {command .Id } not found" );
177-
178- if (await _repository .EmailExistsAsync (command .Email , command .Id ))
179- return Result .Conflict (" Another user already has this email address" );
180-
181- var updatedUser = existingUser with { Name = command .Name , Email = command .Email };
182- await _repository .UpdateAsync (updatedUser );
183-
184- return Result .Ok (updatedUser );
185- }
186-
187- public async Task <Result > HandleAsync (DeleteUserCommand command )
188- {
189- var deleted = await _repository .DeleteAsync (command .Id );
190- return deleted
191- ? Result .NoContent ()
192- : Result .NotFound ($" User with ID {command .Id } not found" );
193- }
194- }
195- ```
196-
197- ## 🔄 Cascading Messages - Elegant Event Choreography
198-
199- Foundatio.Mediator supports ** cascading messages** through tuple return types, enabling elegant event choreography where handlers can trigger additional messages automatically. This is perfect for implementing event-driven workflows and the saga pattern.
200-
201- ### How Cascading Messages Work
202-
203- When a handler returns a tuple, the mediator automatically:
204-
205- 1 . ** Returns the expected type** to the caller (if specified)
206- 2 . ** Publishes remaining tuple items** as cascading events using ` PublishAsync `
207- 3 . ** Waits for completion** - all cascading messages are processed before the original call completes
208-
209- ### Example: Order Processing with Cascading Events
210-
211- ``` csharp
212- // Messages
213- public record CreateOrder (string ProductName , decimal Amount , string CustomerEmail );
214- public record Order (int Id , string ProductName , decimal Amount , string CustomerEmail , DateTime CreatedAt );
215- public record OrderCreatedEvent (int OrderId , string CustomerEmail , decimal Amount );
216- public record SendWelcomeEmail (string Email , string CustomerName );
217- public record UpdateInventory (string ProductName , int Quantity );
218-
219- // Handler that returns a tuple - triggers cascading messages
220- public class CreateOrderHandler
221- {
222- public async Task <(Order , OrderCreatedEvent )> HandleAsync (CreateOrder command , CancellationToken cancellationToken )
223- {
224- // Create the order
225- var order = new Order (
226- Id : Random .Shared .Next (1000 , 9999 ),
227- ProductName : command .ProductName ,
228- Amount : command .Amount ,
229- CustomerEmail : command .CustomerEmail ,
230- CreatedAt : DateTime .UtcNow
231- );
232-
233- // Create the event that will be published automatically
234- var orderCreatedEvent = new OrderCreatedEvent (order .Id , order .CustomerEmail , order .Amount );
235-
236- // Return tuple - Order goes to caller, OrderCreatedEvent gets published
237- return (order , orderCreatedEvent );
238- }
239- }
240-
241- // Handler for the cascading event
242- public class OrderCreatedEventHandler
243- {
244- public async Task HandleAsync (OrderCreatedEvent orderCreated , CancellationToken cancellationToken )
245- {
246- Console .WriteLine ($" Order {orderCreated .OrderId } created for ${orderCreated .Amount }" );
247-
248- // Could trigger more cascading messages by returning a tuple
249- // For example: return (new SendWelcomeEmail(orderCreated.CustomerEmail, "Valued Customer"),);
250- }
251- }
252- ```
253-
254- ### Usage with Cascading Messages
255-
256- ``` csharp
257- // Only the Order is returned to the caller
258- // OrderCreatedEvent is automatically published to all its handlers
259- var order = await mediator .InvokeAsync <Order >(new CreateOrder (
260- ProductName : " Amazing Widget" ,
261- Amount : 29 . 99 m ,
262- CustomerEmail : " customer@example.com"
263- ));
264-
265- Console .WriteLine ($" Created order {order .Id } - cascading events processed automatically!" );
266- ```
267-
268- ### Multiple Cascading Messages
269-
270- Handlers can return tuples with multiple cascading messages:
271-
272- ``` csharp
273- public class ComplexOrderHandler
274- {
275- public async Task <(Order , OrderCreatedEvent , SendWelcomeEmail , UpdateInventory )> HandleAsync (
276- CreateOrder command ,
277- CancellationToken cancellationToken )
278- {
279- var order = new Order (/* ...*/ );
280-
281- return (
282- order , // Returned to caller
283- new OrderCreatedEvent (order .Id , order .CustomerEmail , order .Amount ), // Published
284- new SendWelcomeEmail (order .CustomerEmail , " Valued Customer" ), // Published
285- new UpdateInventory (order .ProductName , - 1 ) // Published
286- );
287- }
288- }
289- ```
290-
291155## 📊 Performance Benchmarks
292156
293157Foundatio.Mediator delivers exceptional performance, getting remarkably close to direct method calls while providing full mediator pattern benefits:
0 commit comments