MediatR.Courier 6.0.0

dotnet add package MediatR.Courier --version 6.0.0                
NuGet\Install-Package MediatR.Courier -Version 6.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="MediatR.Courier" Version="6.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add MediatR.Courier --version 6.0.0                
#r "nuget: MediatR.Courier, 6.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// 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 Nuget Quality Gate Status Coverage

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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