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(this IServiceCollection services) where TQuery : class where TQueryHandler : class, IQueryHandler { // add handler to DI. services.AddTransient, TQueryHandler>(); // add for discovery purposes. var queryMeta = new QueryMeta(typeof(TQuery), typeof(IQueryHandler), typeof(TQueryResult)); services.AddSingleton(queryMeta); return services; } public static IServiceCollection AddCommand(this IServiceCollection services) where TCommand : class where TCommandHandler : class, ICommandHandler { // add handler to DI. services.AddTransient, TCommandHandler>(); // add for discovery purposes. var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler), typeof(TCommandResult)); services.AddSingleton(commandMeta); return services; } public static IServiceCollection AddCommand(this IServiceCollection services) where TCommand : class where TCommandHandler : class, ICommandHandler { // add handler to DI. services.AddTransient, TCommandHandler>(); // add for discovery purposes. var commandMeta = new CommandMeta(typeof(TCommand), typeof(ICommandHandler)); services.AddSingleton(commandMeta); return services; } /// /// Scans the specified assembly and registers all command handlers found. /// /// The service collection /// The assembly to scan for command handlers /// Optional filter to include/exclude specific handler types /// Service lifetime for handlers (default: Transient) /// The service collection for chaining public static IServiceCollection AddCommandsFromAssembly( this IServiceCollection services, Assembly assembly, Func? 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 RegisterCommandHandler(services, commandType, handlerType, null, lifetime); } else if (genericArgs.Length == 2) { // ICommandHandler var resultType = genericArgs[1]; RegisterCommandHandler(services, commandType, handlerType, resultType, lifetime); } } return services; } /// /// Scans the specified assembly and registers all query handlers found. /// /// The service collection /// The assembly to scan for query handlers /// Optional filter to include/exclude specific handler types /// Service lifetime for handlers (default: Transient) /// The service collection for chaining public static IServiceCollection AddQueriesFromAssembly( this IServiceCollection services, Assembly assembly, Func? 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 serviceType = typeof(ICommandHandler<>).MakeGenericType(commandType); } else { // ICommandHandler 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(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(queryMeta); } }