如何在IServiceCollection的AddScoped中使用泛型接口并注入所有相关Builder?
解决泛型Builder类的DI注册与批量注入问题
1. 让泛型接口支持协变
默认情况下,泛型接口IBuilder<T>是不变的,这意味着IBuilder<SquareFeetRow>无法被当作IBuilder<IOrganizationNames>使用。要解决这个问题,需要给泛型参数添加out关键字开启协变:
public interface IBuilder<out T> where T : IOrganizationNames { ... }
协变允许将派生类型的泛型接口实例赋值给基类型的泛型接口变量,正好匹配你需要将所有IBuilder<T>(T实现IOrganizationNames)统一注入为IBuilder<IOrganizationNames>的场景。
2. 自动批量注册所有Builder类
不用逐个手动注册每个Builder,通过反射扫描程序集中符合约定的类型,自动完成注册:
编写扩展方法
using System.Reflection; public static class ServiceCollectionExtensions { public static IServiceCollection AddAllBuilders(this IServiceCollection services, Assembly targetAssembly) { // 筛选所有符合条件的Builder类:非抽象、非泛型、实现了IBuilder<T>且T继承自IOrganizationNames var builderTypes = targetAssembly.GetTypes() .Where(type => !type.IsAbstract && !type.IsGenericType && type.GetInterfaces().Any(@interface => @interface.IsGenericType && @interface.GetGenericTypeDefinition() == typeof(IBuilder<>)) && typeof(IOrganizationNames).IsAssignableFrom(@interface.GetGenericArguments()[0]))); foreach (var builderType in builderTypes) { // 获取该Builder对应的泛型接口(比如IBuilder<SquareFeetRow>) var genericInterface = builderType.GetInterfaces() .First(@interface => @interface.IsGenericType && @interface.GetGenericTypeDefinition() == typeof(IBuilder<>)); // 1. 注册为具体泛型接口(支持直接注入IBuilder<SquareFeetRow>) services.AddScoped(genericInterface, builderType); // 2. 注册为IBuilder<IOrganizationNames>(支持批量注入) services.AddScoped(typeof(IBuilder<IOrganizationNames>), provider => provider.GetRequiredService(genericInterface)); } return services; } }
在Program.cs中调用扩展方法
// 传入包含Builder类的程序集(示例为当前执行程序集) builder.Services.AddAllBuilders(Assembly.GetExecutingAssembly());
3. 验证注入效果
现在你的MyService中注入IEnumerable<IBuilder<IOrganizationNames>>就能正常获取所有Builder实例了:
public sealed class MyService { public MyService(IEnumerable<IBuilder<IOrganizationNames>> builders) { // builders集合会包含所有注册的Builder实例 } }
内容的提问来源于stack exchange,提问作者Gargoyle
相关产品推荐
相关产品推荐

