This is a simple pub/sub implementation for .NET applications. It allows you to easily publish messages to subscribers and manage subscriptions. Yes, this is yet another pub/sub implementation for .NET applications! Am I reinventing the wheel? Almost certainly, but I wanted to create a simple implementation that is easy for me to use and for me to understand. I also wanted to improve my knowledge of the pub/sub pattern and how it can be implemented in .NET applications. At the timeof writing my role has moved away from software engineering and into a more integration based role, so I wanted to keep my skills sharp and continue to learn.
this library has taken a lot of inspiration from the following libraries:
At the point of writing both libraries have far more fuinctionailty than this library, but the driver is to create a simple implementation that is easy to use and understand. In addition it appears the later version of MediatR require licensing for commercial use, which is not the case for this library. This is a key driver for me, as the organisation I work for has a number of internal applications that require pub/sub functionality, and this library is developed being used in those applications.
TBC.
Dibware.PubSub supports Microsoft.Extensions.DependencyInjection.Abstractions directly. To register Dibware.PubSub.SimpleMediator services and handlers:
There are three available notification registration modes for notification handlers: two automatic and one manual. The default registration mode is manual.
The two automatic modes are using assembly scanning to find and register notification handlers, or speccifying the notification handler types explicitly.
Set NotificationRegistrationMode to RegisterFromAssemblies to use automatic registration mode with assembly scanning.
services.AddSimpleMediator(options =>
{
options.NotificationRegistrationMode = Registration.NotificationRegistrationMode.RegisterFromAssemblies;
});If register from assemblies mode is set then the system requires the user to specify the assemblies to scan for notification handlers.
For example:
services.AddSimpleMediator(options =>
{
options.NotificationRegistrationMode = Registration.NotificationRegistrationMode.RegisterFromAssemblies;
options.AssembliesToScanForNotifications = new List<Assembly>
{
typeof(FakeUserUnRegisteredEventNotificationHandler).Assembly
};
});Set NotificationRegistrationMode to RegisterFromTypes to use automatic registration mode with explicit type registration.
services.AddSimpleMediator(options =>
{
options.NotificationRegistrationMode = Registration.NotificationRegistrationMode.RegisterFromTypes;
});If register from types mode is set then the system requires the user to specify the notification handler types to register.
For example:
services.AddSimpleMediator(options =>
{
options.NotificationRegistrationMode = Registration.NotificationRegistrationMode.RegisterFromTypes;
options.TypesToRegisterForNotifications = new List<Type>
{
typeof(FakeUserUnRegisteredEventNotificationHandler)
};
});Set NotificationRegistrationMode to ManualRegistration to use manual registration mode.
services.AddSimpleMediator(options =>
{
options.NotificationRegistrationMode = Registration.NotificationRegistrationMode.ManualRegistration;
});If manual mode is set then the system requires the user to register notification handlers manually.
services.AddScoped(typeof(INotificationHandler<UserUnRegisteredEvent>), typeof(FakeUserUnRegisteredEventNotificationHandler));
⚠️ Currently the component only allows a service dependency binding a single concrete class handler for a given handler interface. During automatic registration using an assembly, an exception is thrown if two concrete classes are bound to the same interface are encountered. No exceptions are thrown for Automatic binding via types, however you may experince off behaviour with only one of the handlers firing. No exceptions are thrown for for manual binding. As long as you keep a reference to the handler that you bind the event handling will work. See unit tests.
There are two available processing modes for notification handlers: sequential and parallel. The default processing mode is sequential.
services.AddSimpleMediator(options =>
{
options.ProcessingMode = Registration.NotificationPublisherProcessingMode.Sequential;
});services.AddSimpleMediator(options =>
{
options.ProcessingMode = Registration.NotificationPublisherProcessingMode.Parallel;
});The configuration options are available via the ConfigurationOptions class. The following options are available:
- Type:
List<Assembly> - Default: Empty collection
- Type:
List<Type> - Default: Empty collection
- Type:
NotificationRegistrationMode(enum) - Default:
ManualRegistration
Sets the registration mode for notification handlers. The following options are available:
ManualRegistration: Notification handlers must be registered manually.RegisterFromAssemblies: Notification handlers will be automatically registered from the specified assemblies.RegisterFromTypes: Notification handlers will be automatically registered from the specified types.
- Type:
NotificationPublisherProcessingMode(enum) - Default:
NotificationPublisherProcessingMode.Sequential
Sets the processing mode for notification handlers. The following options are available:
Sequential: Notification handlers will be executed sequentially, one after the other.Parallel: Notification handlers will be executed in parallel, usingTask.WhenAll.
Events can be classes or records and carry any data your require, but they must inherit from INotification.
/// <summary>
/// Represents an event where a user was registered.
/// </summary>
/// <param name="UserName">Represents the user name.</param>
/// <param name="Email">Represents the email address.</param>
public record UserRegisteredEvent(string UserName, string Email) : INotification;Handlers must implement INotificationHandler<TNotification>, where TNotification is a class or record that implements INotification. For example the UserRegisteredEvent shown above.
/// <summary>
/// Represents a handler for the UserRegisteredEvent used in unit tests.
/// Once the Handle method is called, the HandleCalled property will be set to true.
/// </summary>
public class UserRegisteredEventNotificationHandler : INotificationHandler<UserRegisteredEvent>
{
public Task Handle(UserRegisteredEvent _notification, CancellationToken cancellationToken)
{
// Add the code to respond to the event being raised, here.
// Add the code to respond to the event being raised, here.
}
}You are welcome to contribute by bringing ideas to the table or working on ideas or bugs which others have raised.
Feel free to fork this repo an have a play around.
masteris (and should always be) the production ready branchdevelopis for collating complete features witha view to being release to master*feature/my-funky-featureis for actively devloping features
*initially until there are more contributers than me, I will probably work straight in develop! I will do better once we have more contributers than me!