dotnet-cqrs/Svrnty.CQRS.Abstractions/ServiceCollectionExtensions.cs

180 lines
7.5 KiB
C#

using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using Svrnty.CQRS.Abstractions.Discovery;
namespace Svrnty.CQRS.Abstractions;
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddQuery<TQuery, TQueryResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TQueryHandler>(this IServiceCollection services)
where TQuery : class
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
{
// add handler to DI.
services.AddTransient<IQueryHandler<TQuery, TQueryResult>, TQueryHandler>();
// add for discovery purposes.
var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler<TQuery, TQueryResult>), typeof(TQueryResult));
services.AddSingleton<IQueryMeta>(queryMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, TCommandResult, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand, TCommandResult>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand, TCommandResult>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand, TCommandResult>), typeof(TCommandResult));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
public static IServiceCollection AddCommand<TCommand, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TCommandHandler>(this IServiceCollection services)
where TCommand : class
where TCommandHandler : class, ICommandHandler<TCommand>
{
// add handler to DI.
services.AddTransient<ICommandHandler<TCommand>, TCommandHandler>();
// add for discovery purposes.
var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler<TCommand>));
services.AddSingleton<ICommandMeta>(commandMeta);
return services;
}
/// <summary>
/// Scans the specified assembly and registers all command handlers found.
/// </summary>
/// <param name="services">The service collection</param>
/// <param name="assembly">The assembly to scan for command handlers</param>
/// <param name="filter">Optional filter to include/exclude specific handler types</param>
/// <param name="lifetime">Service lifetime for handlers (default: Transient)</param>
/// <returns>The service collection for chaining</returns>
public static IServiceCollection AddCommandsFromAssembly(
this IServiceCollection services,
Assembly assembly,
Func<Type, bool>? filter = null,
ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var commandHandlerInterfaces = assembly.GetTypes()
.Where(type => type.IsClass && !type.IsAbstract && !type.IsGenericTypeDefinition)
.Where(type => filter == null || filter(type))
.SelectMany(handlerType => handlerType.GetInterfaces()
.Where(i => i.IsGenericType &&
(i.GetGenericTypeDefinition() == typeof(ICommandHandler<>) ||
i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)))
.Select(interfaceType => new { HandlerType = handlerType, InterfaceType = interfaceType }))
.ToList();
foreach (var registration in commandHandlerInterfaces)
{
var genericArgs = registration.InterfaceType.GetGenericArguments();
var commandType = genericArgs[0];
var handlerType = registration.HandlerType;
if (genericArgs.Length == 1)
{
// ICommandHandler<TCommand>
RegisterCommandHandler(services, commandType, handlerType, null, lifetime);
}
else if (genericArgs.Length == 2)
{
// ICommandHandler<TCommand, TResult>
var resultType = genericArgs[1];
RegisterCommandHandler(services, commandType, handlerType, resultType, lifetime);
}
}
return services;
}
/// <summary>
/// Scans the specified assembly and registers all query handlers found.
/// </summary>
/// <param name="services">The service collection</param>
/// <param name="assembly">The assembly to scan for query handlers</param>
/// <param name="filter">Optional filter to include/exclude specific handler types</param>
/// <param name="lifetime">Service lifetime for handlers (default: Transient)</param>
/// <returns>The service collection for chaining</returns>
public static IServiceCollection AddQueriesFromAssembly(
this IServiceCollection services,
Assembly assembly,
Func<Type, bool>? filter = null,
ServiceLifetime lifetime = ServiceLifetime.Transient)
{
var queryHandlerInterfaces = assembly.GetTypes()
.Where(type => type.IsClass && !type.IsAbstract && !type.IsGenericTypeDefinition)
.Where(type => filter == null || filter(type))
.SelectMany(handlerType => handlerType.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IQueryHandler<,>))
.Select(interfaceType => new { HandlerType = handlerType, InterfaceType = interfaceType }))
.ToList();
foreach (var registration in queryHandlerInterfaces)
{
var genericArgs = registration.InterfaceType.GetGenericArguments();
var queryType = genericArgs[0];
var resultType = genericArgs[1];
var handlerType = registration.HandlerType;
RegisterQueryHandler(services, queryType, resultType, handlerType, lifetime);
}
return services;
}
private static void RegisterCommandHandler(
IServiceCollection services,
Type commandType,
Type handlerType,
Type? resultType,
ServiceLifetime lifetime)
{
// Register handler with DI
Type serviceType;
if (resultType == null)
{
// ICommandHandler<TCommand>
serviceType = typeof(ICommandHandler<>).MakeGenericType(commandType);
}
else
{
// ICommandHandler<TCommand, TResult>
serviceType = typeof(ICommandHandler<,>).MakeGenericType(commandType, resultType);
}
services.Add(new ServiceDescriptor(serviceType, handlerType, lifetime));
// Register metadata for discovery
var commandMeta = resultType == null
? new CommandMeta(commandType, serviceType)
: new CommandMeta(commandType, serviceType, resultType);
services.AddSingleton<ICommandMeta>(commandMeta);
}
private static void RegisterQueryHandler(
IServiceCollection services,
Type queryType,
Type resultType,
Type handlerType,
ServiceLifetime lifetime)
{
// Register handler with DI
var serviceType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
services.Add(new ServiceDescriptor(serviceType, handlerType, lifetime));
// Register metadata for discovery
var queryMeta = new QueryMeta(queryType, serviceType, resultType);
services.AddSingleton<IQueryMeta>(queryMeta);
}
}