Skip to content
 
 

Repository files navigation

Dibware.PubSub

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.

Getting Started

TBC.

Registering with IServiceCollection

Dibware.PubSub supports Microsoft.Extensions.DependencyInjection.Abstractions directly. To register Dibware.PubSub.SimpleMediator services and handlers:

Notification Registration Mode

There are three available notification registration modes for notification handlers: two automatic and one manual. The default registration mode is manual.

Automatic Registration Mode

The two automatic modes are using assembly scanning to find and register notification handlers, or speccifying the notification handler types explicitly.

Register by Assembly Scanning

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
        };
    });
Register From Types

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)
        };
    });

Manual Registration Mode

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));

Limitaions

⚠️ 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.

Processing Mode

There are two available processing modes for notification handlers: sequential and parallel. The default processing mode is sequential.

Using sequential processing mode (the default mode):

services.AddSimpleMediator(options =>
    {
        options.ProcessingMode = Registration.NotificationPublisherProcessingMode.Sequential;
    });
Using parallel processing mode:
services.AddSimpleMediator(options =>
    {
        options.ProcessingMode = Registration.NotificationPublisherProcessingMode.Parallel;
    });

Configuration Options

The configuration options are available via the ConfigurationOptions class. The following options are available:

AssembliesToScanForNotifications

  • Type: List<Assembly>
  • Default: Empty collection

TypesToRegisterForNotifications

  • Type: List<Type>
  • Default: Empty collection

NotificationRegistrationMode

  • 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.

ProcessingMode

  • 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, using Task.WhenAll.

Creating Events To Publish

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;

Creating Handlers for Events

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.
    }
}

Contributing

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.

Branching

  • master is (and should always be) the production ready branch
  • develop is for collating complete features witha view to being release to master*
  • feature/my-funky-feature is 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!

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages