Skip to content

Commit 6b8e319

Browse files
committed
Various updates
1 parent c92917b commit 6b8e319

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+749
-363
lines changed

README.md

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ Foundatio.Mediator is a high-performance, convention-based mediator library that
1010

1111
## ✨ Why Choose Foundatio.Mediator?
1212

13-
- **🚀 Blazing Fast** - Only 2x slower than direct method calls, 3x faster than MediatR
13+
- **🚀 Blazing Fast** - Nearly as fast as direct method calls, 3x faster than MediatR
1414
- **🎯 Convention-Based** - No interfaces or base classes required
1515
- **⚡ Source Generated** - Compile-time code generation for optimal performance
1616
- **🔧 Full DI Integration** - Works seamlessly with Microsoft.Extensions.DependencyInjection
17-
- **🎪 Middleware Pipeline** - Elegant middleware support with logging, timing, and custom logic
17+
- **🎪 Middleware Pipeline** - Elegant middleware support
1818
- **📦 Auto Registration** - Handlers discovered and registered automatically
1919
- **🔒 Compile-Time Safety** - Rich diagnostics catch errors before runtime
2020
- **🔧 C# Interceptors** - Direct method calls using cutting-edge C# interceptor technology
@@ -67,7 +67,7 @@ var mediator = serviceProvider.GetRequiredService<IMediator>();
6767
await mediator.InvokeAsync(new PingCommand("123"));
6868

6969
// Request/response
70-
var greeting = await mediator.InvokeAsync<string>(new GreetingQuery("World"));
70+
var greeting = mediator.Invoke<string>(new GreetingQuery("World"));
7171
Console.WriteLine(greeting); // "Hello, World!"
7272
```
7373

@@ -107,30 +107,21 @@ public class LoggingMiddleware
107107
}
108108
}
109109

110-
public class GlobalMiddleware
110+
public class ValidationMiddleware
111111
{
112-
public (DateTime Date, TimeSpan Time) Before(object message, CancellationToken cancellationToken)
112+
public HandlerResult Before(object message)
113113
{
114-
Console.WriteLine($"🌍 Processing {message.GetType().Name}");
115-
return (DateTime.UtcNow, DateTime.UtcNow.TimeOfDay);
116-
}
117-
118-
public async Task After(object message, DateTime start, TimeSpan time,
119-
IEmailService emailService, CancellationToken cancellationToken)
120-
{
121-
await emailService.SendEmailAsync("audit@company.com", "Message Processed",
122-
$"Message {message.GetType().Name} processed at {start}");
123-
Console.WriteLine($"🌍 Completed {message.GetType().Name}");
124-
}
125-
126-
public void Finally(object message, Exception? exception, CancellationToken cancellationToken)
127-
{
128-
if (exception != null)
114+
if (!TryValidate(message, out var errors))
129115
{
130-
Console.WriteLine($"🌍 Error: {exception.Message}");
116+
// If validation fails, short-circuit the handler execution
117+
return HandlerResult.ShortCircuit(Result.Invalid(errors));
131118
}
119+
120+
return HandlerResult.Continue();
132121
}
133122
}
123+
124+
var user = mediator.InvokeAsync<Result<User>>(new GetUserQuery(userId), cancellationToken);
134125
```
135126

136127
## 💉 Dependency Injection Made Simple
@@ -274,12 +265,11 @@ dotnet run
274265

275266
The source generator:
276267

277-
1. **Discovers handlers** at compile time by scanning for classes ending with "Handler" or "Consumer"
278-
2. **Validates method signatures** to ensure they follow the conventions
268+
1. **Discovers handlers** at compile time by scanning for classes ending with `Handler` or `Consumer`
269+
2. **Discovers handler methods** looks for methods with names like `Handle`, `HandleAsync`, `Consume`, `ConsumeAsync`
270+
3. **Parameters** first parameter is the message, remaining parameters are injected via DI
279271
3. **Generates C# interceptors** for blazing fast same-assembly dispatch using direct method calls
280-
4. **Registers keyed handlers in DI** as fallback for cross-assembly scenarios and publish operations
281-
5. **Creates middleware pipelines** with proper before/after/finally execution order
282-
6. **Generates handler registrations** that span multiple projects for comprehensive handler discovery
272+
4. **Middleware** with can run `Before`, `After`, and `Finally` around handler execution and can be sync or async
283273

284274
### 🔧 C# Interceptors - The Secret Sauce
285275

benchmarks/Foundatio.Mediator.Benchmarks/ThroughputBenchmarks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public async Task FoundatioQueryThroughputAsync(int queryCount)
9898
var query = _greetingQueries[i % _greetingQueries.Length];
9999
tasks[i] = _foundatioMediator.InvokeAsync<string>(query).AsTask();
100100
}
101-
var results = await Task.WhenAll(tasks);
101+
string[] results = await Task.WhenAll(tasks);
102102
}
103103

104104
[Benchmark]

samples/ConsoleSample/Handlers/CalculationHandlers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ public class SyncCalculationHandler
77
{
88
public string Handle(SyncCalculationQuery query)
99
{
10-
var result = query.A + query.B;
10+
int result = query.A + query.B;
1111
Console.WriteLine($"🧮 Sync calculation: {query.A} + {query.B} = {result}");
1212
return $"Sum: {result}";
1313
}
@@ -18,7 +18,7 @@ public class AsyncCalculationHandler
1818
public async Task<string> HandleAsync(AsyncCalculationQuery query, CancellationToken cancellationToken = default)
1919
{
2020
await Task.Delay(50, cancellationToken); // Simulate async work
21-
var result = query.A * query.B;
21+
int result = query.A * query.B;
2222
Console.WriteLine($"🧮 Async calculation: {query.A} * {query.B} = {result}");
2323
return $"Product: {result}";
2424
}

samples/ConsoleSample/Handlers/DependencyInjectionHandlers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public async Task HandleAsync(
1313
IGreetingService greetingService,
1414
CancellationToken cancellationToken = default)
1515
{
16-
var greeting = greetingService.CreateGreeting(command.Name);
16+
string greeting = greetingService.CreateGreeting(command.Name);
1717
await emailService.SendEmailAsync(
1818
command.Email,
1919
"Welcome!",

samples/ConsoleSample/Handlers/OrderHandlers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class OrderEmailNotificationHandler(EmailNotificationService emailService
88
{
99
public async Task HandleAsync(OrderCreatedEvent orderEvent)
1010
{
11-
var message = $"Order {orderEvent.OrderId} has been created for ${orderEvent.Amount:F2}";
11+
string message = $"Order {orderEvent.OrderId} has been created for ${orderEvent.Amount:F2}";
1212
await emailService.SendAsync(message);
1313
}
1414
}
@@ -20,7 +20,7 @@ public async Task HandleAsync(
2020
SmsNotificationService smsService,
2121
CancellationToken cancellationToken = default)
2222
{
23-
var message = $"Your order {orderEvent.OrderId} is confirmed!";
23+
string message = $"Your order {orderEvent.OrderId} is confirmed!";
2424
await smsService.SendAsync(message);
2525
}
2626
}

samples/ConsoleSample/Messages/ValidationMessages.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ public class CreateUserCommand : ICommand
77
{
88
[Required(ErrorMessage = "Name is required")]
99
[StringLength(50, MinimumLength = 2, ErrorMessage = "Name must be between 2 and 50 characters")]
10-
public string Name { get; set; } = string.Empty;
10+
public string Name { get; set; } = String.Empty;
1111

1212
[Required(ErrorMessage = "Email is required")]
1313
[EmailAddress(ErrorMessage = "Invalid email format")]
14-
public string Email { get; set; } = string.Empty;
14+
public string Email { get; set; } = String.Empty;
1515

1616
[Range(18, 120, ErrorMessage = "Age must be between 18 and 120")]
1717
public int Age { get; set; }
@@ -23,8 +23,8 @@ public class CreateUserCommand : ICommand
2323
public record User
2424
{
2525
public int Id { get; set; }
26-
public string Name { get; set; } = string.Empty;
27-
public string Email { get; set; } = string.Empty;
26+
public string Name { get; set; } = String.Empty;
27+
public string Email { get; set; } = String.Empty;
2828
public int Age { get; set; }
2929
public string? PhoneNumber { get; set; }
3030
public DateTime CreatedAt { get; set; }

samples/ConsoleSample/Middleware/GlobalMiddleware.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
using ConsoleSample.Services;
2-
31
namespace ConsoleSample.Middleware;
42

53
public class GlobalMiddleware
@@ -10,12 +8,6 @@ public class GlobalMiddleware
108
return (DateTime.UtcNow, DateTime.UtcNow.TimeOfDay);
119
}
1210

13-
public async Task After(object message, DateTime start, TimeSpan time, IEmailService emailService, CancellationToken cancellationToken)
14-
{
15-
await emailService.SendEmailAsync("test@test.com", $"Hello", $"Message of type {message.GetType().Name} processed successfully at {DateTime.UtcNow}");
16-
Console.WriteLine($"🌍 [GlobalMiddleware] After: Completed processing {message.GetType().Name} {start}");
17-
}
18-
1911
public void Finally(object message, Exception? exception, CancellationToken cancellationToken)
2012
{
2113
if (exception != null)

samples/ConsoleSample/Middleware/ProcessOrderMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public Task<HandlerResult> BeforeAsync(ProcessOrderCommand command, Cancellation
1010
Console.WriteLine($"🔸 [ProcessOrderMiddleware] Before: Processing order {command.OrderId} with type {command.ProcessingType}");
1111

1212
// Add some validation logic
13-
if (string.IsNullOrWhiteSpace(command.OrderId))
13+
if (String.IsNullOrWhiteSpace(command.OrderId))
1414
{
1515
Console.WriteLine($"🔸 [ProcessOrderMiddleware] Invalid order ID, short-circuiting");
1616
return Task.FromResult(HandlerResult.ShortCircuit("Invalid order ID"));

samples/ConsoleSample/Middleware/ValidationMiddleware.cs

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,13 @@
33

44
namespace ConsoleSample.Middleware;
55

6-
/// <summary>
7-
/// Validation middleware that uses MiniValidator to validate message models before handler execution.
8-
/// If validation fails, it short-circuits and returns validation errors as a Result.
9-
/// </summary>
106
public class ValidationMiddleware
117
{
12-
/// <summary>
13-
/// Validates the message before handler execution. If validation fails, returns a short-circuit result.
14-
/// </summary>
15-
/// <param name="message">The message to validate.</param>
16-
/// <returns>A HandlerResult that either continues or short-circuits with validation errors.</returns>
178
public HandlerResult Before(object message)
189
{
19-
if (!MiniValidator.TryValidate(message, out var errors))
10+
if (!TryValidate(message, out var errors))
2011
{
21-
// Convert MiniValidator errors to Foundatio.Mediator ValidationErrors
22-
var validationErrors = errors.SelectMany(kvp =>
23-
kvp.Value.Select(errorMessage =>
24-
new ValidationError(kvp.Key, errorMessage))).ToList();
25-
26-
return HandlerResult.ShortCircuit(Result.Invalid(validationErrors));
12+
return HandlerResult.ShortCircuit(Result.Invalid(errors));
2713
}
2814

2915
return HandlerResult.Continue();
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"ConsoleSample": {
5+
"commandName": "Project",
6+
"environmentVariables": {
7+
8+
}
9+
}
10+
}
11+
}

0 commit comments

Comments
 (0)