From b6bb5d5b0fa52152db435e37c2c2b1143f6f365e Mon Sep 17 00:00:00 2001 From: Daniel Clark Date: Fri, 16 Dec 2022 12:30:52 +0000 Subject: [PATCH] Mediator work --- .../Extensions/AssemblyExtensions.cs | 12 ++ .../IServiceCollectionExtensions.cs | 104 ++++++++++++++---- .../Lifecycles/CommandClassHandlerWrapper.cs | 25 +++++ Framework/Foundation/Lifecycles/Mediator.cs | 64 ++++++++++- .../Lifecycles/QueryClassHandlerWrapper.cs | 26 +++++ 5 files changed, 204 insertions(+), 27 deletions(-) create mode 100644 Framework/Foundation/Extensions/AssemblyExtensions.cs create mode 100644 Framework/Foundation/Lifecycles/CommandClassHandlerWrapper.cs create mode 100644 Framework/Foundation/Lifecycles/QueryClassHandlerWrapper.cs diff --git a/Framework/Foundation/Extensions/AssemblyExtensions.cs b/Framework/Foundation/Extensions/AssemblyExtensions.cs new file mode 100644 index 0000000..05de80a --- /dev/null +++ b/Framework/Foundation/Extensions/AssemblyExtensions.cs @@ -0,0 +1,12 @@ +using System.Reflection; + +namespace Toolkit.Framework.Foundation; + +public static class AssemblyExtensions +{ + public static Stream? ExtractResource(this Assembly assembly, string filename) + { + string? resourceName = $"{assembly.GetName()?.Name?.Replace("-", "_")}.{filename}"; + return assembly.GetManifestResourceStream(resourceName); + } +} diff --git a/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs b/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs index fd4d095..9371ec3 100644 --- a/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs +++ b/Framework/Foundation/Extensions/IServiceCollectionExtensions.cs @@ -1,34 +1,20 @@ using Mediator; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; namespace Toolkit.Framework.Foundation; public static class IServiceCollectionExtensions { - public static IServiceCollection AddHandler(this IServiceCollection services) where TRequestHandler : notnull + public static IServiceCollection AddConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration) where TConfiguration : class, new() { - if (typeof(TRequestHandler).GetInterface(typeof(IRequestHandler<,>).Name) is { } contract) - { - if (contract.GetGenericArguments() is { Length: 2 } arguments) - { - Type requestType = arguments[0]; - Type responseType = arguments[1]; - Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + serviceCollection.Configure(configuration); + serviceCollection.AddTransient(provider => provider.GetService>()!.CurrentValue); + serviceCollection.AddTransient>(); - services.TryAdd(new ServiceDescriptor(typeof(TRequestHandler), typeof(TRequestHandler), ServiceLifetime.Transient)); - services.Add(new ServiceDescriptor(wrapperType, - sp => - { - return sp.GetService()?.Create(wrapperType, sp.GetRequiredService(), sp.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!; - }, - ServiceLifetime.Transient - )); - - } - } - - return services; + return serviceCollection; } public static IServiceCollection AddFoundation(this IServiceCollection serviceCollection) @@ -50,4 +36,80 @@ public static class IServiceCollectionExtensions return serviceCollection; } + + public static IServiceCollection AddHandler(this IServiceCollection services, ServiceLifetime lifetime = ServiceLifetime.Transient) where THandler : notnull + { + if (typeof(THandler).GetInterface(typeof(IRequestHandler<,>).Name) is { } requestContract) + { + if (requestContract.GetGenericArguments() is { Length: 2 } arguments) + { + Type requestType = arguments[0]; + Type responseType = arguments[1]; + + Type wrapperType = typeof(RequestClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + + services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); + services.Add(new ServiceDescriptor(wrapperType, + sp => + { + return sp.GetService()?.Create(wrapperType, sp.GetRequiredService(), sp.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!; + }, + lifetime + )); + + } + } + + if (typeof(THandler).GetInterface(typeof(ICommandHandler<,>).Name) is { } commandContract) + { + if (commandContract.GetGenericArguments() is { Length: 2 } arguments) + { + Type requestType = arguments[0]; + Type responseType = arguments[1]; + + Type wrapperType = typeof(CommandClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + + services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); + services.Add(new ServiceDescriptor(wrapperType, + sp => + { + return sp.GetService()?.Create(wrapperType, sp.GetRequiredService(), sp.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!; + }, + lifetime + )); + } + } + + if (typeof(THandler).GetInterface(typeof(IQueryHandler<,>).Name) is { } queryContract) + { + if (queryContract.GetGenericArguments() is { Length: 2 } arguments) + { + Type requestType = arguments[0]; + Type responseType = arguments[1]; + + Type wrapperType = typeof(QueryClassHandlerWrapper<,>).MakeGenericType(requestType, responseType); + + services.TryAdd(new ServiceDescriptor(typeof(THandler), typeof(THandler), lifetime)); + services.Add(new ServiceDescriptor(wrapperType, + sp => + { + return sp.GetService()?.Create(wrapperType, sp.GetRequiredService(), sp.GetServices(typeof(IPipelineBehavior<,>).MakeGenericType(requestType, responseType)))!; + }, + lifetime + )); + } + } + return services; + } + + public static IServiceCollection AddWritableConfiguration(this IServiceCollection serviceCollection, IConfiguration configuration) where TConfiguration : class, new() + { + serviceCollection.Configure(configuration); + serviceCollection.AddTransient, ConfigurationWriter>(); + serviceCollection.AddTransient(provider => provider.GetService>()!.CurrentValue); + serviceCollection.AddHandler>(); + serviceCollection.AddTransient>(); + + return serviceCollection; + } } \ No newline at end of file diff --git a/Framework/Foundation/Lifecycles/CommandClassHandlerWrapper.cs b/Framework/Foundation/Lifecycles/CommandClassHandlerWrapper.cs new file mode 100644 index 0000000..828e6e3 --- /dev/null +++ b/Framework/Foundation/Lifecycles/CommandClassHandlerWrapper.cs @@ -0,0 +1,25 @@ +using Mediator; + +namespace Toolkit.Framework.Foundation; + +public class CommandClassHandlerWrapper where TRequest : class, ICommand +{ + private readonly MessageHandlerDelegate handler; + + public CommandClassHandlerWrapper(ICommandHandler concreteHandler, + IEnumerable> pipelineBehaviours) + { + MessageHandlerDelegate handler = concreteHandler.Handle; + + foreach (IPipelineBehavior? pipeline in pipelineBehaviours.Reverse()) + { + MessageHandlerDelegate handlerCopy = handler; + IPipelineBehavior pipelineCopy = pipeline; + handler = (TRequest message, CancellationToken cancellationToken) => pipelineCopy.Handle(message, cancellationToken, handlerCopy); + } + + this.handler = handler; + } + + public ValueTask Handle(TRequest request, CancellationToken cancellationToken) => handler(request, cancellationToken); +} diff --git a/Framework/Foundation/Lifecycles/Mediator.cs b/Framework/Foundation/Lifecycles/Mediator.cs index 94693a8..9c76897 100644 --- a/Framework/Foundation/Lifecycles/Mediator.cs +++ b/Framework/Foundation/Lifecycles/Mediator.cs @@ -1,5 +1,4 @@ using Mediator; -using System.Diagnostics; namespace Toolkit.Framework.Foundation; @@ -44,8 +43,7 @@ public class Mediator : IMediator public ValueTask Send(IRequest request, CancellationToken cancellationToken = default) { - dynamic? handler = factory.GetService(typeof(RequestClassHandlerWrapper<,>).MakeGenericType(request.GetType(), typeof(TResponse))); - + dynamic? handler = factory.GetService(typeof(RequestClassHandlerWrapper<,>).MakeGenericType(request.GetType(), typeof(TResponse))); if (handler is not null) { return handler.Handle((dynamic)request, cancellationToken); @@ -56,16 +54,70 @@ public class Mediator : IMediator public ValueTask Send(ICommand command, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + dynamic? handler = factory.GetService(typeof(CommandClassHandlerWrapper<,>).MakeGenericType(command.GetType(), typeof(TResponse))); + if (handler is not null) + { + return handler.Handle((dynamic)command, cancellationToken); + } + + return default; } public ValueTask Send(IQuery query, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + dynamic? handler = factory.GetService(typeof(QueryClassHandlerWrapper<,>).MakeGenericType(query.GetType(), typeof(TResponse))); + if (handler is not null) + { + return handler.Handle((dynamic)query, cancellationToken); + } + + return default; } public ValueTask Send(object message, CancellationToken cancellationToken = default) { - throw new NotImplementedException(); + if (message.GetType().GetInterface(typeof(IRequest<>).Name) is { } requestInterface) + { + if (requestInterface.GetGenericArguments() is { Length: 1 } arguments) + { + Type responseType = arguments[0]; + + dynamic? handler = factory.GetService(typeof(RequestClassHandlerWrapper<,>).MakeGenericType(message.GetType(), responseType)); + if (handler is not null) + { + return handler.Handle((dynamic)message, cancellationToken); + } + } + } + + if (message.GetType().GetInterface(typeof(ICommand<>).Name) is { } commandInterface) + { + if (commandInterface.GetGenericArguments() is { Length: 1 } arguments) + { + Type responseType = arguments[0]; + + dynamic? handler = factory.GetService(typeof(CommandClassHandlerWrapper<,>).MakeGenericType(message.GetType(), responseType)); + if (handler is not null) + { + return handler.Handle((dynamic)message, cancellationToken); + } + } + } + + if (message.GetType().GetInterface(typeof(IQuery<>).Name) is { } queryInterface) + { + if (queryInterface.GetGenericArguments() is { Length: 1 } arguments) + { + Type responseType = arguments[0]; + + dynamic? handler = factory.GetService(typeof(QueryClassHandlerWrapper<,>).MakeGenericType(message.GetType(), responseType)); + if (handler is not null) + { + return handler.Handle((dynamic)message, cancellationToken); + } + } + } + + return default; } } diff --git a/Framework/Foundation/Lifecycles/QueryClassHandlerWrapper.cs b/Framework/Foundation/Lifecycles/QueryClassHandlerWrapper.cs new file mode 100644 index 0000000..6f36b67 --- /dev/null +++ b/Framework/Foundation/Lifecycles/QueryClassHandlerWrapper.cs @@ -0,0 +1,26 @@ +using Mediator; + +namespace Toolkit.Framework.Foundation; + +public class QueryClassHandlerWrapper where TRequest : class, IQuery +{ + private readonly MessageHandlerDelegate handler; + + public QueryClassHandlerWrapper(IQueryHandler concreteHandler, + IEnumerable> pipelineBehaviours) + { + MessageHandlerDelegate handler = concreteHandler.Handle; + + foreach (IPipelineBehavior? pipeline in pipelineBehaviours.Reverse()) + { + MessageHandlerDelegate handlerCopy = handler; + IPipelineBehavior pipelineCopy = pipeline; + handler = (TRequest message, CancellationToken cancellationToken) => pipelineCopy.Handle(message, cancellationToken, handlerCopy); + } + + this.handler = handler; + } + + public ValueTask Handle(TRequest request, CancellationToken cancellationToken) => + handler(request, cancellationToken); +}