How to Use
š Get Startedā
Use the same patterns you know from MediatR, but with Mediahater interfaces and classes.
For Full Package:
dotnet add package Mediahater
For Abstractive-only Package:
dotnet add package Mediahater.Abstractions
Migration from MediatRā
Unless you use Pipleline features from MediatR you should be able to substitute their code for ours.
Do NOT do this on your main branch. Always back up your project or work in a separate branch.
Search for all MediatR.
adn replace with Medihater.
.
Search for all IMediator
and replace with IMedihator
.
Register Servicesā
Mediahater uses a different pattern when it comes the service registration.
Manual Registrationā
services.AddMedihaterServices();
// With Return Value
services.AddMedihaterRequestHandler<GetArticleQuery, GetArticleHandler, ArticleResponse>();
services.AddMedihaterRequestHandler<CreateArticleCommand, CreateArticleHandler>();
// Add Notifications
services.AddMedihaterNotificationHandler<SpreadMeNotification, SpreadListennerOneHandler>();
services.AddMedihaterNotificationHandler<SpreadMeNotification, SpreadListennerTwoHandler>();
Auto Scanā
services.AddMedihaterServices(cfg =>
{
cfg.AssembliesScan = [
typeof(Program)
];
});
Request/Handlersā
Void Task Requestā
public record CreateArticleCommand : IRequest
{
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
}
internal class CreateArticleHandler : IRequestHandler<CreateArticleCommand>
{
public async Task Handle(CreateArticleCommand request, CancellationToken cancellationToken)
{
await Task.Delay(100);
}
}
Request Task with resultā
public record ArticleResponse
{
public string Title { get; init; } = default!;
public string Description { get; init; } = default!;
}
public record GetArticleQuery(string Id) : IRequest<ArticleResponse>
{
}
internal class GetArticleHandler : IRequestHandler<GetArticleQuery, ArticleResponse>
{
public async Task<ArticleResponse> Handle(GetArticleQuery request, CancellationToken cancellationToken)
{
await Task.Delay(500);
return new ArticleResponse()
{
Description = "My Description",
Title = "The Title"
};
}
}
Send the Requestā
Use the usual IMedihator.Send
to call handler.
Sending without Data Resukt:
_medihater = _serviceProvider.GetRequiredService<IMedihater>();
var command = new CreateArticleCommand() {
Title = "",
Description = ""
};
await _medihater.Send(command);
Sending wiht Data Result:
_medihater = _serviceProvider.GetRequiredService<IMedihater>();
var response = await _medihater.Send(new GetArticleQuery("dsad"));
Notificationsā
Define the Notificationā
public record SpreadMeNotification(string Message) : INotification
{
}
internal class SpreadListennerOneHandler : INotificationHandler<SpreadMeNotification>
{
public async Task Handle(SpreadMeNotification notification, CancellationToken cancellationToken)
{
await Task.Delay(500);
// my first hook
}
}
internal class SpreadListennerOneHandler : INotificationHandler<SpreadMeNotification>
{
public async Task Handle(SpreadMeNotification notification, CancellationToken cancellationToken)
{
await Task.Delay(500);
// My second hook
}
}
### Publish Notification
Use the usual ```IMedihator.Publish``` to call handler.
```csharp
_medihater = _serviceProvider.GetRequiredService<IMedihater>();
var notification = new SpreadMeNotification("My Message");
await _medihater.Publish(notification);
Performance & Customizationā
There are several ways you can optimize and control how the pipeline behaves in Mediahater.
Out of the box, Mediahater is highly optimized. During development, we went from being 5.65Ć slower than MediatR to approximately 1.16Ć faster than MediatR for equivalent dispatch operations.
Iām not a benchmarking guru, but the results look very promising.
How the Pipeline Worksā
- We use interface-based middleware to pass down events.
- Middlewares are intended to be used as auditors only.
Customize Your Pipelineā
You have options to define how the pipeline behaves:
- Set PipelineNotificationMiddleware strategy:
NeverAwaitMiddlewares
OnlyAwaitOrderedExecuted
(Default)AwaitAllMiddlewares
- Choose how publish middleware interacts with handlers:
WaitMiddleBeforePulish_To_TriggerNotificationHandlers
TriggerNotificationHandler_Then_AwaitForBoth
(Default)
- Control request and notification flow.
- Select invocation strategies based on your performance needs.
Publish Middleware Strategiesā
WaitMiddleBeforePulish_To_TriggerNotificationHandlers
ā
- Executes
IPublishMiddleware.BeforePublish
and awaits all middlewares before publishing notifications. - Guarantees middlewares complete before any handlers execute.
- Good for strict ordering and consistency.
TriggerNotificationHandler_Then_AwaitForBoth
(Default)ā
- Runs all middlewares, then notifications, then awaits everything together.
- No guarantee that middleware completes before its associated handler executes.
- Higher concurrency, less consistency.
Note: The
PipelineNotificationMiddleware
setting affects this behavior.
Notification Publish Strategiesā
NotificationFireMode = PipelineNotificationFireMode.FireAndForget
(Default)ā
- Publish Notifications if it fails you don't care.
NotificationFireMode = PipelineNotificationFireMode.FireAllAndAwait
ā
- Publish Notification to all related handlers and await for all to finish.
NotificationFireMode = PipelineNotificationFireMode.FireOneAndAwait
ā
- Publish Notification to each of the related handlers and await.
Pipeline Notification Middleware Optionsā
NeverAwaitMiddlewares
ā
- Fire-and-forget ā does not await middlewares.
- Can cause inconsistencies such as
AfterPublish
triggering beforeBeforePublish
. - Best for maximum speed, parallel scenarios.
OnlyAwaitOrderedExecuted
(Default)ā
- Awaits ordered execution, guaranteeing
BeforePublish
completes before publishing starts. - Balanced performance and consistency.
AwaitAllMiddlewares
ā
- Awaits everything (Before + After + all notifications).
- Highest consistency, but adds overhead.
- The more middlewares, the slower it gets.
Middleware Settingsā
services.AddMedihaterServices(o =>
{
o.MiddlewareAwaitMode = Configuraions.Enums.PipelineMiddlewareWaitMode.NeverAwaitMiddlewares;
o.NotificationMiddleware = Configuraions.Enums.PipelineNotificationMiddleware.TriggerNotificationHandler_Then_AwaitForBoth;
});
Caching Settingsā
EagerCaching
ā
- Cache all at startup
- Heavy traffic or low-latency APIs where every millisecond matters.
- Systems where handlers are stable and known at startup.
LazyCaching
(Default)ā
- Cache as you go.
- Apps with many handlers but rarely hit all of them.
- Low/medium traffic environments where startup time matters more than first-call performance.
services.AddMedihaterServices(o =>
{
o.CachingMode = Configuraions.Enums.PipelineCachingMode.LazyCaching;
});