feat: create request projects for basic CQRS
This commit is contained in:
commit
d614788e06
190 changed files with 12236 additions and 0 deletions
174
src/request.dispatcher/RequestDispatcherBuilderExtensions.cs
Normal file
174
src/request.dispatcher/RequestDispatcherBuilderExtensions.cs
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
// Copyright (c) The Geekeey Authors
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
using static Geekeey.Request.Dispatcher.RequestDispatcherOptions;
|
||||
|
||||
namespace Geekeey.Request.Dispatcher;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for configuring <see cref="IRequestDispatcherBuilder"/>
|
||||
/// with additional capabilities such as searching and registering request handlers in assemblies or adding types directly.
|
||||
/// </summary>
|
||||
public static class RequestDispatcherBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Searches for request handler types within the specified assembly and adds them to the request dispatcher
|
||||
/// configuration.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Prefer the generated <c>Add<Name>(builder)</c> extensions for assemblies that directly reference
|
||||
/// <c>Geekeey.Request.Dispatcher</c>. This runtime scanning API remains supported, but nested request handlers are rejected
|
||||
/// during registration and generated registration should not be mixed with <see cref="SearchHandlerInAssembly(IRequestDispatcherBuilder, Assembly)"/>
|
||||
/// for the same assembly.
|
||||
/// </remarks>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> to configure.</param>
|
||||
/// <param name="assembly">The assembly to search for request handler types.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder SearchHandlerInAssembly(this IRequestDispatcherBuilder builder, Assembly assembly)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
var exports = assembly.GetTypes()
|
||||
.Where(type => type is { IsClass: true, IsAbstract: false })
|
||||
.Where(IsRequestHandlerImplementationType);
|
||||
|
||||
builder.Add(exports);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for request handler types within the specified assembly and adds them to the request dispatcher
|
||||
/// configuration with the given service lifetime.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Prefer the generated <c>Add<Name>(builder, lifetime)</c> extensions for assemblies that directly reference
|
||||
/// <c>Geekeey.Request.Dispatcher</c>. This runtime scanning API remains supported, but nested request handlers are rejected
|
||||
/// during registration and generated registration should not be mixed with <see cref="SearchHandlerInAssembly(IRequestDispatcherBuilder, Assembly, ServiceLifetime)"/>
|
||||
/// for the same assembly.
|
||||
/// </remarks>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> to configure.</param>
|
||||
/// <param name="assembly">The assembly to search for request handler types.</param>
|
||||
/// <param name="lifetime">The lifetime with which the request handlers are registered in the dependency injection container.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder SearchHandlerInAssembly(this IRequestDispatcherBuilder builder, Assembly assembly, ServiceLifetime lifetime)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
var exports = assembly.GetTypes()
|
||||
.Where(type => type is { IsClass: true, IsAbstract: false })
|
||||
.Where(IsRequestHandlerImplementationType);
|
||||
|
||||
builder.Add(exports, lifetime);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified type to the request dispatcher configuration for inspection.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> to configure.</param>
|
||||
/// <param name="type">The type to be added to the request dispatcher configuration.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder Add(this IRequestDispatcherBuilder builder, Type type)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
ValidateNoNestedRequestHandlers([type]);
|
||||
|
||||
builder.Services.AddOptions<RequestDispatcherOptions>()
|
||||
.Configure(options => options.Inspect([type]));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified type to the request dispatcher configuration.
|
||||
/// This also adds the type to the service collection with the specified lifetime,
|
||||
/// allowing it to be resolved as a dependency in request handlers and behaviors.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> used to configure the request dispatcher.</param>
|
||||
/// <param name="type">The type to be added to the request dispatcher configuration.</param>
|
||||
/// <param name="lifetime">The lifetime scope of the type in the service container.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder Add(this IRequestDispatcherBuilder builder, Type type, ServiceLifetime lifetime)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
ValidateNoNestedRequestHandlers([type]);
|
||||
|
||||
builder.Services.AddOptions<RequestDispatcherOptions>()
|
||||
.Configure(options => options.Inspect([type]));
|
||||
|
||||
builder.Services.Add(new ServiceDescriptor(type, type, lifetime));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified collection of types to the request dispatcher configuration for inspection.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> to configure.</param>
|
||||
/// <param name="type">The collection of types to be added to the request dispatcher configuration.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder Add(this IRequestDispatcherBuilder builder, IEnumerable<Type> type)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
var types = ValidateNoNestedRequestHandlers([.. type]);
|
||||
|
||||
builder.Services.AddOptions<RequestDispatcherOptions>()
|
||||
.Configure(options => options.Inspect(types));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified collection of types to the request dispatcher configuration for inspection.
|
||||
/// This also adds the specified collection of types to the service collection with the specified lifetime,
|
||||
/// allowing it to be resolved as a dependency in request handlers and behaviors.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IRequestDispatcherBuilder"/> to configure.</param>
|
||||
/// <param name="type">The collection of types to be added to the request dispatcher configuration.</param>
|
||||
/// <param name="lifetime">The lifetime scope of the types in the service container.</param>
|
||||
/// <returns>The <see cref="IRequestDispatcherBuilder"/> instance for further configuration.</returns>
|
||||
public static IRequestDispatcherBuilder Add(this IRequestDispatcherBuilder builder, IEnumerable<Type> type, ServiceLifetime lifetime)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(builder);
|
||||
|
||||
var types = ValidateNoNestedRequestHandlers([.. type]);
|
||||
|
||||
builder.Services.AddOptions<RequestDispatcherOptions>()
|
||||
.Configure(options => options.Inspect(types));
|
||||
|
||||
builder.Services.Add(types.Select(export => new ServiceDescriptor(export, export, lifetime)));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IReadOnlyCollection<Type> ValidateNoNestedRequestHandlers(IReadOnlyCollection<Type> types, [CallerMemberName] string? invoker = null)
|
||||
{
|
||||
var nestedHandlers = types
|
||||
.Where(type => type is { IsClass: true, IsAbstract: false, IsNested: true })
|
||||
.Where(IsRequestHandlerImplementationType)
|
||||
.Select(type => type.FullName ?? type.Name)
|
||||
.OrderBy(static name => name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
|
||||
if (nestedHandlers.Length > 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Nested request handlers are not supported by {invoker}: {string.Join(", ", nestedHandlers)}");
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
private static bool IsRequestHandlerImplementationType(Type type)
|
||||
{
|
||||
return type.GetInterfaces().Any(IsRequestHandlerType);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue