Skip to content

Commit

Permalink
refactor: Refactored mediator interfaces, got rid of object overloads…
Browse files Browse the repository at this point in the history
… and default implementations
  • Loading branch information
jhartmann123 committed Aug 8, 2024
1 parent cc73761 commit b41796a
Show file tree
Hide file tree
Showing 17 changed files with 256 additions and 180 deletions.
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.1.1</Version>
<Version>9.2.0</Version>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

Expand Down
1 change: 1 addition & 0 deletions src/Mediator/src/ContainerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

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

Expand Down
14 changes: 1 addition & 13 deletions src/Mediator/src/IAsyncEnumerableRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Fusonic.Extensions.Mediator;
/// </summary>
/// <typeparam name="TRequest">The type of request being handled</typeparam>
/// <typeparam name="TResponse">The type of response from the handler</typeparam>
public interface IAsyncEnumerableRequestHandler<in TRequest, out TResponse> : IAsyncEnumerableRequestHandlerBase<TResponse>
public interface IAsyncEnumerableRequestHandler<in TRequest, out TResponse>
where TRequest : IAsyncEnumerableRequest<TResponse>
{
/// <summary>
Expand All @@ -18,16 +18,4 @@ public interface IAsyncEnumerableRequestHandler<in TRequest, out TResponse> : IA
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Response from the request</returns>
IAsyncEnumerable<TResponse> Handle(TRequest request, CancellationToken cancellationToken);

IAsyncEnumerable<TResponse> IAsyncEnumerableRequestHandlerBase<TResponse>.Handle(object request, CancellationToken cancellationToken)
=> Handle((TRequest)request, cancellationToken);
}

/// <summary>
/// Base Request Handler that can be casted to, without knowing the request type
/// </summary>
/// <typeparam name="TResponse">The type of the response</typeparam>
public interface IAsyncEnumerableRequestHandlerBase<out TResponse>
{
IAsyncEnumerable<TResponse> Handle(object request, CancellationToken cancellationToken);
}
13 changes: 1 addition & 12 deletions src/Mediator/src/INotificationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Fusonic.Extensions.Mediator;
/// Defines a handler for a notification
/// </summary>
/// <typeparam name="TNotification">The type of notification being handled</typeparam>
public interface INotificationHandler<in TNotification> : INotificationHandlerBase
public interface INotificationHandler<in TNotification>
where TNotification : INotification
{
/// <summary>
Expand All @@ -16,15 +16,4 @@ public interface INotificationHandler<in TNotification> : INotificationHandlerBa
/// <param name="notification">The notification</param>
/// <param name="cancellationToken">Cancellation token</param>
Task Handle(TNotification notification, CancellationToken cancellationToken);

async Task INotificationHandlerBase.Handle(object notification, CancellationToken cancellationToken)
=> await Handle((TNotification)notification, cancellationToken);
}

/// <summary>
/// Base Notification Handler that can be casted to, without knowing the request type
/// </summary>
public interface INotificationHandlerBase
{
Task Handle(object notification, CancellationToken cancellationToken);
}
14 changes: 1 addition & 13 deletions src/Mediator/src/IRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Fusonic.Extensions.Mediator;
/// </summary>
/// <typeparam name="TRequest">The type of request being handled</typeparam>
/// <typeparam name="TResponse">The type of response from the handler</typeparam>
public interface IRequestHandler<in TRequest, TResponse> : IRequestHandlerBase<TResponse>
public interface IRequestHandler<in TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
/// <summary>
Expand All @@ -18,9 +18,6 @@ public interface IRequestHandler<in TRequest, TResponse> : IRequestHandlerBase<T
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Response from the request</returns>
Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);

async Task<TResponse> IRequestHandlerBase<TResponse>.Handle(object request, CancellationToken cancellationToken)
=> await Handle((TRequest)request, cancellationToken);
}

/// <summary>
Expand All @@ -32,15 +29,6 @@ public interface IRequestHandler<in TRequest> : IRequestHandler<TRequest, Unit>
where TRequest : IRequest<Unit>
{ }

/// <summary>
/// Base Request Handler that can be casted to, without knowing the request type
/// </summary>
/// <typeparam name="TResponse">The type of the response</typeparam>
public interface IRequestHandlerBase<TResponse>
{
Task<TResponse> Handle(object request, CancellationToken cancellationToken);
}

/// <summary>
/// Wrapper class for a handler that asynchronously handles a request and does not return a response
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Runtime.CompilerServices;
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal sealed class AsyncEnumerableRequestHandlerWrapper<TRequest, TResponse> : IAsyncEnumerableRequestHandlerWrapper
where TRequest : IAsyncEnumerableRequest<TResponse>
{
public async IAsyncEnumerable<object> Handle(object request, Container container, [EnumeratorCancellation] CancellationToken cancellationToken)
{
var handlers = container.GetInstance<IAsyncEnumerableRequestHandler<TRequest, TResponse>>();
await foreach (var item in handlers.Handle((TRequest)request, cancellationToken))
{
yield return item!;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal interface IAsyncEnumerableRequestHandlerWrapper
{
IAsyncEnumerable<object> Handle(object request, Container container, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal interface INotificationHandlerWrapper
{
Task Handle(object notification, Container container, CancellationToken cancellationToken);
}
8 changes: 8 additions & 0 deletions src/Mediator/src/SimpleInjector/IRequestHandlerWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal interface IRequestHandlerWrapper
{
Task<object> Handle(object request, Container container, CancellationToken cancellationToken);
}
16 changes: 16 additions & 0 deletions src/Mediator/src/SimpleInjector/NotificationHandlerWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal sealed class NotificationHandlerWrapper<T> : INotificationHandlerWrapper
where T : INotification
{
public async Task Handle(object notification, Container container, CancellationToken cancellationToken)
{
var handlers = container.GetAllInstances<INotificationHandler<T>>();
foreach (var handler in handlers)
{
await handler.Handle((T)notification, cancellationToken);
}
}
}
15 changes: 15 additions & 0 deletions src/Mediator/src/SimpleInjector/RequestHandlerWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Fusonic GmbH. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for license information.

using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

internal sealed class RequestHandlerWrapper<TRequest, TResponse> : IRequestHandlerWrapper
where TRequest : IRequest<TResponse>
{
public async Task<object> Handle(object request, Container container, CancellationToken cancellationToken)
{
return (await container.GetInstance<IRequestHandler<TRequest, TResponse>>().Handle((TRequest)request, cancellationToken))!;
}
}
69 changes: 69 additions & 0 deletions src/Mediator/src/SimpleInjector/SimpleInjectorMediator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Fusonic GmbH. All rights reserved.
// Licensed under the MIT License. See LICENSE file in the project root for license information.

using System.Collections.Concurrent;
using System.Runtime.CompilerServices;
using SimpleInjector;

namespace Fusonic.Extensions.Mediator.SimpleInjector;

public class SimpleInjectorMediator(Container container) : IMediator
{
private static readonly ConcurrentDictionary<Type, IRequestHandlerWrapper> RequestWrappers = new();
private static readonly ConcurrentDictionary<Type, INotificationHandlerWrapper> NotificationWrappers = new();
private static readonly ConcurrentDictionary<Type, IAsyncEnumerableRequestHandlerWrapper> AsyncEnumWrappers = new();

public async Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);

var wrapper = RequestWrappers.GetOrAdd(request.GetType(),
t =>
{
var wrapperType = typeof(RequestHandlerWrapper<,>).MakeGenericType(t, typeof(TResponse));
var wrapperInstance = Activator.CreateInstance(wrapperType) as IRequestHandlerWrapper
?? throw new ActivationException($"Could not create wrapper for type {t.FullName}.");

return wrapperInstance;
});

return (TResponse)await wrapper.Handle(request, container, cancellationToken);
}

public async Task Publish(INotification notification, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(notification);

var wrapper = NotificationWrappers.GetOrAdd(notification.GetType(),
t =>
{
var wrapperType = typeof(NotificationHandlerWrapper<>).MakeGenericType(t);
var wrapperInstance = Activator.CreateInstance(wrapperType) as INotificationHandlerWrapper
?? throw new ActivationException($"Could not create wrapper for type {t.FullName}.");

return wrapperInstance;
});

await wrapper.Handle(notification, container, cancellationToken);
}

public async IAsyncEnumerable<TResponse> CreateAsyncEnumerable<TResponse>(IAsyncEnumerableRequest<TResponse> request, [EnumeratorCancellation] CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);

var wrapper = AsyncEnumWrappers.GetOrAdd(request.GetType(),
t =>
{
var wrapperType = typeof(AsyncEnumerableRequestHandlerWrapper<,>).MakeGenericType(t, typeof(TResponse));
var wrapperInstance = Activator.CreateInstance(wrapperType) as IAsyncEnumerableRequestHandlerWrapper
?? throw new ActivationException($"Could not create wrapper for type {t.FullName}.");

return wrapperInstance;
});

await foreach (var item in wrapper.Handle(request, container, cancellationToken))
{
yield return (TResponse)item;
}
}
}
44 changes: 0 additions & 44 deletions src/Mediator/src/SimpleInjectorMediator.cs

This file was deleted.

Loading

0 comments on commit b41796a

Please sign in to comment.