何时用GetRequiredService()而非直接注册类?Redis场景解惑
背景代码
原Startup.ConfigureServices()中的服务注册代码如下:
... services .AddSingleton(x => { IConnectionMultiplexer connection = ... string keyPrefix = ... return new RedisSessionService(connection, keyPrefix); }) .AddSingleton<ITicketStore>(x => x.GetRequiredService<RedisSessionService>()) .AddSingleton<ISessionService>(x => x.GetRequiredService<RedisSessionService>()) .AddSingleton<ILookupNormalizer, LowerInvariantLookupNormalizer>() ...
其中RedisSessionService同时实现了ITicketStore(来自Microsoft.AspNetCore.Authentication.Cookies命名空间)和自定义接口ISessionService。
错误场景
若改用直接注册方式:
.AddSingleton<ITicketStore, RedisSessionService>() .AddSingleton<ISessionService, RedisSessionService>()
则会抛出以下错误:
Unable to resolve service for type 'System.String' while attempting to activate 'MyNamespace.IdentityServer4.Server.Services.RedisSessionService'.
疑问
- 此场景下为何必须使用
GetRequiredService()? - 何时应使用
GetRequiredService(),何时应使用AddSingleton<ISomeInterface, SomeClass>()?
一、此场景必须用GetRequiredService()的原因
核心问题出在RedisSessionService的构造函数依赖上:它需要IConnectionMultiplexer和string类型的keyPrefix参数,而string是简单值类型,DI容器无法自动解析这类未注册的基础类型(DI只能自动解析已在容器中注册的服务实例,不会凭空生成一个字符串)。
原代码中,你通过工厂委托AddSingleton(x => { ... })手动完成了RedisSessionService的实例化——手动传入了connection和keyPrefix,同时把这个实例以自身类型RedisSessionService注册到了DI容器中。
而改用AddSingleton<ITicketStore, RedisSessionService>()这种方式时,DI容器会尝试自动构造RedisSessionService实例,但它找不到string类型的注册项,因此抛出无法解析System.String的错误。
此时使用GetRequiredService<RedisSessionService>(),本质是告诉DI容器:不要重新创建新的RedisSessionService实例,直接复用之前通过工厂委托创建好的那个实例,将其映射到ITicketStore和ISessionService接口上。这样既保证了单例实例的唯一性,又避免了DI容器尝试解析无法自动处理的构造函数依赖。
二、两种注册方式的适用场景
1. 优先使用AddSingleton<ISomeInterface, SomeClass>()的场景
- 服务类(
SomeClass)的构造函数依赖全部是已注册的服务类型(比如其他接口、自定义类,不包含string、int这类无法自动解析的简单类型)。 - 不需要手动干预实例化过程,希望DI容器自动管理实例的创建逻辑。
- 服务类的实例化逻辑简单,无需额外参数拼接、条件判断等自定义操作。
2. 需要使用GetRequiredService()的场景
- 已通过工厂委托手动创建并注册了某个类的实例,需要将同一个实例映射到多个不同的接口上(如你的场景:一个实例实现两个接口,需同时注册到两个接口)。
- 需要复用已有的实例,避免DI容器创建多个相同类的实例,保证单例唯一性。
- 服务类的实例化需要自定义逻辑,无法通过DI自动解析构造函数依赖(比如需要传入简单类型参数、动态生成的参数等),此时先通过工厂委托创建实例,再用
GetRequiredService将实例映射到对应接口。
内容的提问来源于stack exchange,提问作者David Klempfner

