using System.Reflection; namespace RunnersMeet.Server; public class RequestHandlerModule : IAppConfigurationModule { public void ConfigureServices(IServiceCollection services, IConfigurationRoot config) { services.AddScoped(); Register(services, new List { // More specific first, then fall back to more general new HandlerMatcher(typeof(IRequestHandler<,,>)), new HandlerMatcher(typeof(IRequestHandler<,>)), }); } public void ConfigureApplication(WebApplication app) { } private void Register(IServiceCollection services, IList matchers) { foreach (var candidate in Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsClass)) { var matcher = matchers.FirstOrDefault(m => m.IsMatch(candidate)); if (matcher != null) { foreach (var handlerInterface in matcher.GetHandlerInterfaces(candidate)) { services.AddScoped(handlerInterface, candidate); } } } } private sealed class HandlerMatcher { private readonly Type _genericInterfaceType; public HandlerMatcher(Type genericInterfaceType) { _genericInterfaceType = genericInterfaceType; } public bool IsMatch(Type t) => t.GetInterfaces().Any(i => ImplementsExactly(i, _genericInterfaceType)); public IEnumerable GetHandlerInterfaces(Type t) => t.GetInterfaces().Where(i => ImplementsExactly(i, _genericInterfaceType)); private static bool ImplementsExactly(Type candidate, Type genericInterface) { if (!candidate.IsGenericType) { return false; } var genericTypeDefinition = candidate.GetGenericTypeDefinition(); return /*genericTypeDefinition.GenericTypeArguments.Length == genericInterface.GenericTypeArguments.Length &&*/ genericTypeDefinition == genericInterface; } } }