如何自动注册服务复用EntityService<>实现并控制接口方法权限?
可行方案:通过DI注册控制接口暴露,复用通用实现
你完全不需要编写每个实体对应的FooService、BarService这类重复包装类,直接利用依赖注入容器的注册逻辑就能实现需求:通过注册不同的接口到同一个通用实现类EntityService<T>,以此控制调用方能访问的方法集合,同时复用通用逻辑。
核心思路
EntityService<T>已经实现了两个接口,但调用方能访问哪些方法,取决于注入的接口类型,而非底层实现类。所以只需针对不同实体,在DI容器中注册对应的接口与EntityService<T>的映射关系即可:
1. 单接口注册(限制仅基础方法)
比如对于Foo实体,只允许访问IEntityService<T>的方法,注册方式如下:
// 注册IEntityService<Foo>到EntityService<Foo> services.AddScoped<IEntityService<Foo>, EntityService<Foo>>();
此时注入IEntityService<Foo>的代码,只能调用GetAsync和CreateAsync,即使EntityService<Foo>实现了GetAllAsync,也无法通过IEntityService<Foo>接口调用。
2. 多接口共享实例(开放所有方法)
对于Bar实体,需要开放所有方法,可注册两个接口指向同一个EntityService<Bar>实例:
// 先注册实体服务本身作为实例容器 services.AddScoped<EntityService<Bar>>(); // 将两个接口都映射到同一个实例 services.AddScoped<IEntityService<Bar>>(sp => sp.GetRequiredService<EntityService<Bar>>()); services.AddScoped<IEntityGetAllService<Bar>>(sp => sp.GetRequiredService<EntityService<Bar>>());
这样无论注入IEntityService<Bar>还是IEntityGetAllService<Bar>,拿到的都是同一个实例,且能根据注入的接口类型访问对应的方法。
自动批量注册(避免手动逐个添加)
如果实体数量较多,可以通过程序集扫描+特性标记实现自动注册:
步骤1:定义控制特性
[AttributeUsage(AttributeTargets.Class)] public class AllowGetAllAttribute : Attribute { }
步骤2:给实体类标记特性
// 不允许GetAll的实体 public class Foo : Entity { /* ... */ } // 允许GetAll的实体 [AllowGetAll] public class Bar : Entity { /* ... */ }
步骤3:扫描程序集自动注册
// 获取所有Entity的子类 var entityTypes = Assembly.GetExecutingAssembly() .GetTypes() .Where(t => typeof(Entity).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface); foreach (var entityType in entityTypes) { // 构造EntityService<T>的具体类型 var serviceType = typeof(EntityService<>).MakeGenericType(entityType); // 注册实体服务本身 services.AddScoped(serviceType); // 注册基础接口IEntityService<T> var baseServiceInterface = typeof(IEntityService<>).MakeGenericType(entityType); services.AddScoped(baseServiceInterface, serviceType); // 如果实体标记了AllowGetAll,注册IEntityGetAllService<T> if (entityType.GetCustomAttribute<AllowGetAllAttribute>() != null) { var getAllInterface = typeof(IEntityGetAllService<>).MakeGenericType(entityType); services.AddScoped(getAllInterface, serviceType); } }
这段代码会自动扫描所有实体类,根据是否有AllowGetAll特性,自动注册对应的接口映射,完全复用EntityService<T>的实现,无需编写任何实体专属服务类。
验证效果
- 当注入
IEntityService<Foo>时,只能调用GetAsync和CreateAsync,无法访问GetAllAsync; - 当注入
IEntityGetAllService<Bar>时,能调用GetAllAsync,注入IEntityService<Bar>时能调用基础方法,且两者指向同一个实例。
内容的提问来源于stack exchange,提问作者Addemod C
相关产品推荐
相关产品推荐

