如何通过具体类型而非接口类型获取Asp.Net Core已注册服务?
嘿,我来帮你搞定这个依赖注入的问题!先理清楚咱们当前的情况,再一步步解决。
先回顾现有代码
你已经定义了这些类和接口:
public class ConcreteRepository : IRepository<T>, IEntityOwner { } public interface IRepository<T> { } public interface IEntityOwner { } public class SomeModel { }
并且在Startup里注册了服务:
services.AddScoped<IRepository<SomeModel>, ConcreteRepository>();
目前你只能通过IRepository<SomeModel>这个接口类型获取实例,但你需要直接通过ConcreteRepository的具体类型来拿到实例,尤其是在不能用泛型的特性场景里。
问题根源
问题出在服务注册的逻辑上:你只把ConcreteRepository作为IRepository<SomeModel>的实现类注册到容器里,容器并没有直接注册ConcreteRepository这个具体类型的服务条目,所以直接调用GetService(typeof(ConcreteRepository))肯定返回null。
解决方案
根据你的场景(特性中使用,不能用泛型),推荐两种最实用的办法:
1. 额外注册具体类型(最直接可靠)
在Startup里同时注册ConcreteRepository的具体类型,并且保证接口和具体类型获取的是同一个实例:
// 先注册具体类型 services.AddScoped<ConcreteRepository>(); // 再通过工厂方式注册接口,复用同一个实例 services.AddScoped<IRepository<SomeModel>>(sp => sp.GetRequiredService<ConcreteRepository>());
这样之后,你就可以直接通过具体类型获取实例,转成IEntityOwner:
var concreteRepositoryInstance = httpContext.RequestServices.GetService(typeof(ConcreteRepository)) as IEntityOwner;
这种方式简单直接,而且能保证不管是通过接口还是具体类型获取的都是同一个Scoped实例,符合Asp.Net Core的生命周期要求。
2. 不修改注册代码,通过服务描述查找(适合不能改Startup的场景)
如果没办法修改Startup的注册逻辑,你可以通过容器的服务描述来找到对应实现类型的服务条目,再获取实例:
using Microsoft.Extensions.DependencyInjection; using System.Linq; // 获取所有已注册的服务描述 var serviceDescriptor = httpContext.RequestServices .GetService<IServiceCollection>()? .FirstOrDefault(sd => sd.ImplementationType == typeof(ConcreteRepository)); if (serviceDescriptor != null) { // 通过找到的服务类型获取实例,再转成具体类型 var instance = httpContext.RequestServices.GetService(serviceDescriptor.ServiceType) as ConcreteRepository; var entityOwner = instance as IEntityOwner; // 后续使用entityOwner... }
注意:这种方式的前提是IServiceCollection被保留在容器中(默认Asp.Net Core是保留的,但有些自定义容器可能会处理掉),如果同一个具体类型被注册到多个服务接口,你可能需要额外筛选。
不推荐的方式(供参考)
还有一种方式是注册IEntityOwner接口,但这需要你提前知道要关联的实现类,而且如果有多个实现类的话筛选起来麻烦:
services.AddScoped<IEntityOwner, ConcreteRepository>(); services.AddScoped<IRepository<SomeModel>, ConcreteRepository>();
然后通过GetServices<IEntityOwner>()筛选具体类型,但这不如直接注册具体类型方便,尤其是你的场景是特性中直接传入具体类型。
内容的提问来源于stack exchange,提问作者philipp-fx

