MediatR.Courier
6.0.0
dotnet add package MediatR.Courier --version 6.0.0
NuGet\Install-Package MediatR.Courier -Version 6.0.0
<PackageReference Include="MediatR.Courier" Version="6.0.0" />
paket add MediatR.Courier --version 6.0.0
#r "nuget: MediatR.Courier, 6.0.0"
// Install MediatR.Courier as a Cake Addin #addin nuget:?package=MediatR.Courier&version=6.0.0 // Install MediatR.Courier as a Cake Tool #tool nuget:?package=MediatR.Courier&version=6.0.0
MediatR.Courier
A simple library to treat MediatR notifications sort of like events.
Main usage target is client applications.
What does this solve?
This library is aimed to provide help for client-side applications using the event aggregator pattern with MediatR.
Usage
Install form NuGet
Basic usage:
services
.AddMediatR(c => c.RegisterServicesFromAssemblyContaining(typeof(MyType)))
.AddCourier(typeof(MyType).Assembly);
ICourier _courier;
void SubscribeToCourier()
{
// Subscribe to a specific notification type your want to receive.
_courier.Subscribe<ExampleNotification>(HandleNotification)
}
void HandleNotification(ExampleNotification notification, CancellationToken cancellationToken)
{
//Do your handling logic here.
Console.WriteLine("ExampleNotification handled");
}
void UnsubscribeFromCourier()
{
// Unsubscribe with the same delegate you subscribed with, just like with events.
_courier.UnSubscribe(HandleNotification);
}
// Somewhere else.
private readonly IMediator _mediator;
async Task FireNotification()
{
// Somewhere some class publishes a notification with the mediator.
// Courier is just a specialized INotificationHandler<INotification> implementation.
await _mediator.Publish(new ExampleNotification());
}
Main concepts:
Events become concrete classes:
//Regular class with event
public class EventProducer
{
public event Action ExampleEvent;
}
//Event defined when using Courier
public class ExampleEvent : INotification
{
//Your implementation own implementation properties, methods, etc...
}
Working with events:
// Using an regular C# event:
void Handler()
{
//Implementation
}
EventProducer producer.ExampleEvent += Handler;
EventProducer producer.ExampleEvent -= Handler;
// Using Courier:
void Handler(ExampleEvent notification)
{
//Implementation
}
ICourier courier = ;//Create courier
courier.Subscribe<ExampleEvent>(Handler);
courier.UnSubscribe<ExampleEvent>(Handler);
Firing events
// Regular C# events
public class EventProducer
{
public event Action ExampleEvent;
public void RaiseEvent()
{
//Implementation
ExampleEvent?.Invoke();
}
}
// Courier
public class EventProducer
{
private IMediator _mediator = ;//Get mediator
public void RaiseEvent()
{
_mediator.Publish(new ExampleNotification());
}
}
What benefits does it provide?
- Your classes handling events don't need a direct reference to a specific instance of an event producer, or some middleman connecting them, just a reference to the Courier
- If the handler is "far" away from the event producer, you don't need to chain events through other classes to receive them
How is this different from MediatR's INotificationHandler?
MediatR instantiates the classes implementing INotificationHandler through the provided ServiceProvider which is most of the time is some dependency injection container, thus it is rather hard to tie handling an event to a specific event handler.
Let's say you have your View which contains two windows. Both windows have the same ViewModel class. You want this ViewModel class to handle some event. What are some choices to handle the lifetime and state of those view models?
- You can make your ViewModel singleton, and when MediatR publishes the notification to the single view model instance. This creates the possible problem that your ViewModels can't hold different state in your windows.
- Have some service handle the notification, then publish it's contents as a regular C# event and then get the reference to that exact service instance to your ViewModels and subscribe to that event. This way you can easily have an individual state in multiple ViewModels, but you need some way to connect them to MediatR notifications.
Courier is a simple implementation of the second choice: Let some middleman handle notifications from MediatR and then pass those notifications to subscribers
Isn't this pattern implemented already in some other libraries?
Yes, quite a few actually, just a few examples:
The difference from those implementations is that most of them implement this concept as part of their framework. When using them in Zenject or Prism you tie some of your implementation to those frameworks, thus having you depend on code you might not want to use. This library has one core dependency: MediatR, and it assumes very little about your architecture. If you are already using MediatR in your business logic layer then adding some reactiveness to it with Courier is easy.
In the same way as using MediatR can be thought of as replacing business layer services to MediatR Requests and RequestHandlers, Courier is the same for events: replacing them with INotifications.
Weak references
You can create a weak subscription by using the SubscribeWeak
method. This subscription uses a WeakReference
which will let the subscriber to be garbage collected without the need to unsubscribe (although you still can unsubscribe manually). Subscribing methods on MonoBehavior
instances in Unity3D might result in unexpected behavior, so you should be careful with it.
courier.SubscribeWeak<MyNotification>(notification => /*...*/);
courier.SubscribeWeak<MyNotification>((notification, cancellation) => /*...*/);
Capturing thread context
You can configure how the Courier awaits the sent notifications. To change the behavior modify the CaptureThreadContext
property on the CourierOptions
class. When using dependency injection, you can change this behavior during runtime, because the CourierOptions
is accessible through DI.
Gotchas
- No ordering is guaranteed when calling the subscribed methods
- Async void methods are not awaited.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
NuGet packages (1)
Showing the top 1 NuGet packages that depend on MediatR.Courier:
Package | Downloads |
---|---|
MediatR.Courier.DependencyInjection
A simple library to treat MediatR notifications sort of like events. Main usage target is client applications. |
GitHub repositories (1)
Showing the top 1 popular GitHub repositories that depend on MediatR.Courier:
Repository | Stars |
---|---|
fullstackhero/dotnet-starter-kit
Production Grade Cloud-Ready .NET 9 Starter Kit (Web API + Blazor Client) with Multitenancy Support, and Clean/Modular Architecture that saves roughly 200+ Development Hours! All Batteries Included.
|
Version | Downloads | Last updated |
---|---|---|
6.0.0 | 64,312 | 3/3/2023 |
5.0.0 | 115,640 | 1/24/2022 |
5.0.0-preview0001 | 235 | 1/20/2022 |
4.0.0 | 3,354 | 12/9/2021 |
3.0.1 | 9,300 | 3/16/2021 |
3.0.0 | 580 | 3/16/2021 |
2.1.2 | 2,725 | 7/2/2020 |
2.1.1 | 1,209 | 3/14/2020 |
2.1.0 | 791 | 3/4/2020 |
2.0.0 | 754 | 2/10/2020 |
1.2.0 | 726 | 2/10/2020 |
1.1.0 | 751 | 1/28/2020 |
1.0.1 | 945 | 1/25/2020 |
1.0.0 | 787 | 1/25/2020 |
# 6.0.0
- Update MediatR to version 12
- Deprecate DI package
- Move test and sample code into one project
- Removed support for convention and interface clients
- Multi target .NET standard 2.0 and .NET 6
- Use reproducible builds
- Include Readme and Changelog in package
# 5.0.0
- Update MediatR to version 10
- Update target framework to netstandard2.1