Skip to content

Commit

Permalink
feature: RegisterMediator helper to easily register mediator
Browse files Browse the repository at this point in the history
  • Loading branch information
davidroth committed Jul 29, 2024
1 parent d03087e commit 934fec9
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 31 deletions.
38 changes: 32 additions & 6 deletions docs/Mediator/README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
# Mediator

This project contains a simple implementation of the common mediator pattern. The implementation focused on simplifying version 11.1.0 of the MediatR package. The pipelining was removed and a pure mediator implementation remains.
This project contains a simple implementation of the common mediator pattern. The implementation focused on simplifying version 11.1.0 of the MediatR package. The pipelining was removed (SimpleInjector has a powerful built-in decorator feature) and a pure mediator implementation based on SimleInjector remains.

`ICommand`, `IQuery`: Use those instead of directly using the `IRequest` interfaces to clearly distinguish between commands and queries.
`OutOfBandAttribute`: You can put this on your `Handler`-class. When used together with the [Hangfire-Decorator](../Hangfire/README.md), the execution of the handler will be queued as a background job automatically.

# Registration

To register the mediator as well as your customer command/notification-handlers, use the following extenion where you can provide all the assemblies you want to scan for handlers.

```cs
container.RegisterMediator(services, [ typeof(Program).Assembly ]);
```

## Mediator transaction handling

There are decorators to run all requests and notifications within a transaction.
To enable this feature use the following SimpleInjector-Configuration:
When using the `RegisterMediator` method from above, all requests and notifications will run within a TransactionScope.
To disable this feature, configure as following:

```cs
Container.RegisterDecorator(typeof(IRequestHandler<,>), typeof(TransactionalRequestHandlerDecorator<,>));
Container.RegisterDecorator(typeof(INotificationHandler<>), typeof(TransactionNotificationHandlerDecorator<>));
Container.RegisterSingleton<ITransactionScopeHandler, TransactionScopeHandler>();
container.RegisterMediator(services, [ typeof(Program).Assembly ], configure =>
{
EnableTransactionalDecorators = false;
});
```

## Custom IMediator implementation

When using the `RegisterMediator` method from above, our default SimpleInjectorMediator will be registered.
If you want to use your custom mediator, configure as following:

```cs

container.Options.AllowOverridingRegistrations = true;
container.RegisterSingleton(typeof(IMediator), typeof(MyCustomMediator));

class MyCustomMediator : IMediator
{
...
}
```
2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>9.0.1</Version>
<Version>9.1.0</Version>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

Expand Down
9 changes: 2 additions & 7 deletions src/Email/test/TestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,9 @@ protected sealed override void RegisterCoreDependencies(Container container)
o.CssPath = Path.Combine(path, "email.css");
o.StoreInDirectory = path;
});

var mediatorAssemblies = new[] { typeof(IMediator).Assembly, typeof(SendEmail).Assembly };
container.RegisterSingleton<IMediator, SimpleInjectorMediator>();
container.Register(typeof(IRequestHandler<,>), mediatorAssemblies);

container.Collection.Register(typeof(INotificationHandler<>), mediatorAssemblies);

var services = new ServiceCollection();
container.RegisterMediator(services, [ typeof(SendEmail).Assembly ]);

services.AddRazorComponents();
services.AddRazorPages()
.ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(new CompiledRazorAssemblyPart(Assembly.GetExecutingAssembly())));
Expand Down
11 changes: 3 additions & 8 deletions src/Hangfire/test/TestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License. See LICENSE file in the project root for license information.

using Fusonic.Extensions.Common.Security;
using Fusonic.Extensions.Common.Transactions;
using Fusonic.Extensions.Mediator;
using Fusonic.Extensions.UnitTests.EntityFrameworkCore;
using Fusonic.Extensions.UnitTests.EntityFrameworkCore.Npgsql;
Expand All @@ -27,12 +26,11 @@ protected sealed override void RegisterCoreDependencies(Container container)
{
var services = new ServiceCollection();

RegisterMediator(container);
RegisterMediator(container, services);

container.RegisterOutOfBandDecorators();

container.RegisterInstance(Substitute.For<IUserAccessor>());
container.Register<ITransactionScopeHandler, TransactionScopeHandler>();

RegisterDatabase(services);
RegisterHangfire(services);
Expand All @@ -42,12 +40,9 @@ protected sealed override void RegisterCoreDependencies(Container container)
Container = container;
}

private static void RegisterMediator(Container container)
private static void RegisterMediator(Container container, IServiceCollection services)
{
var mediatorAssemblies = new[] { typeof(IMediator).Assembly, typeof(TestFixture).Assembly };
container.RegisterSingleton<IMediator, SimpleInjectorMediator>();
container.Register(typeof(IRequestHandler<,>), mediatorAssemblies);
container.Collection.Register(typeof(INotificationHandler<>), mediatorAssemblies);
container.RegisterMediator(services, [typeof(TestFixture).Assembly]);
}

private void RegisterDatabase(IServiceCollection services)
Expand Down
45 changes: 45 additions & 0 deletions src/Mediator/src/ContainerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) Fusonic GmbH. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for license information.

using System.Reflection;
using Fusonic.Extensions.Common.Transactions;
using Microsoft.Extensions.DependencyInjection;
using SimpleInjector;

namespace Fusonic.Extensions.Mediator;

public static class ContainerExtensions
{
/// <summary>
/// Registers all <see cref="IRequestHandler{TRequest, TResponse}"/> and <see cref="INotificationHandler{TNotification}"/> in the given <paramref name="assemblies"/> collection.
/// Also registers the <see cref="SimpleInjectorMediator"/> as the mediator implementation for <see cref="IMediator"/>.
/// </summary>
public static void RegisterMediator(this Container container, IServiceCollection services, Assembly[] assemblies, Action<MediatorOptions>? configureOptions = null)
{
var options = new MediatorOptions();
configureOptions?.Invoke(options);

if (options.EnableTransactionalDecorators)
{
container.RegisterDecorator(typeof(IRequestHandler<,>), typeof(TransactionalRequestHandlerDecorator<,>), Lifestyle.Scoped);
container.RegisterDecorator(typeof(INotificationHandler<>), typeof(TransactionalNotificationHandlerDecorator<>), Lifestyle.Scoped);
container.RegisterSingleton<ITransactionScopeHandler, TransactionScopeHandler>();
}

container.RegisterSingleton(typeof(IMediator), typeof(SimpleInjectorMediator));
container.Register(typeof(IRequestHandler<,>), assemblies, Lifestyle.Scoped);
container.Register(typeof(IAsyncEnumerableRequestHandler<,>), assemblies, Lifestyle.Scoped);
container.Collection.Register(typeof(INotificationHandler<>), assemblies, Lifestyle.Scoped);

services.AddSingleton(_ => container.GetInstance<IMediator>());
}

public class MediatorOptions
{
/// <summary>
/// Controls whether <see cref="TransactionalRequestHandlerDecorator{TCommand, TResult}"/> and <see cref="TransactionalNotificationHandlerDecorator{TNotification}"/> are enabled.
/// Default is <c>true</c>.
/// </summary>
public bool EnableTransactionalDecorators { get; set; } = true;
}
}
1 change: 1 addition & 0 deletions src/Mediator/src/Mediator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

<ItemGroup>
<PackageReference Include="SimpleInjector" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
</ItemGroup>

<ItemGroup>
Expand Down
13 changes: 4 additions & 9 deletions src/Mediator/test/TestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,14 @@ protected override void RegisterCoreDependencies(Container container)
services.AddSimpleInjector(container, setup =>
{
setup.AutoCrossWireFrameworkComponents = true;
RegisterMediator(container);
RegisterMediator(container, services);
});

services.BuildServiceProvider().UseSimpleInjector(container);
}

private static void RegisterMediator(Container container)
private static void RegisterMediator(Container container, IServiceCollection services)
{
var mediatorAssemblies = new[] { typeof(TestFixture).Assembly };

container.RegisterSingleton<IMediator, SimpleInjectorMediator>();
container.Register(typeof(IRequestHandler<,>), mediatorAssemblies);
container.Register(typeof(IAsyncEnumerableRequestHandler<,>), mediatorAssemblies);
container.Collection.Register(typeof(INotificationHandler<>), mediatorAssemblies);
container.RegisterMediator(services, [typeof(TestFixture).Assembly]);
}
}
}

0 comments on commit 934fec9

Please sign in to comment.