如何在Scoped服务生命周期中使多个接口共享同一实例
同一Scoped生命周期下让多接口共享实例的解决方案
问题场景
现有接口与实现类定义如下:
public interface IOperation { string OperationId { get; } } public interface IScopedOperation1 : IOperation { } public interface IScopedOperation2 : IOperation { } public class DefaultOperation : IScopedOperation1, IScopedOperation2 { public string OperationId { get; } = Guid.NewGuid().ToString()[^4..]; } public class OperationLogger { private readonly IScopedOperation1 _scopedOperation1; private readonly IScopedOperation2 _scopedOperation2; public OperationLogger( IScopedOperation1 op1, IScopedOperation2 op2) => (_scopedOperation1, _scopedOperation2) = (op1, op2); public void LogOperations() { Console.WriteLine($"Operation1: [ {_scopedOperation1.OperationId} ]"); Console.WriteLine($"Operation2: [ {_scopedOperation2.OperationId} ]"); } }
按常规方式注册服务后:
using IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services .AddScoped<IScopedOperation1, DefaultOperation>() .AddScoped<IScopedOperation2, DefaultOperation>() .AddTransient<OperationLogger>()) .Build(); host.Services.GetService<OperationLogger>().LogOperations();
会发现IScopedOperation1和IScopedOperation2对应的实例OperationId不一致——这是因为DI容器会为每个接口的Scoped注册单独创建实例。
解决方案
方案1:先注册实现类,再映射所有接口到该实例
先将实现类本身注册为Scoped服务,随后让所有相关接口都解析到这个已注册的实例:
using IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services .AddScoped<DefaultOperation>() .AddScoped<IScopedOperation1>(sp => sp.GetRequiredService<DefaultOperation>()) .AddScoped<IScopedOperation2>(sp => sp.GetRequiredService<DefaultOperation>()) .AddTransient<OperationLogger>()) .Build(); host.Services.GetService<OperationLogger>().LogOperations();
方案2:复用已注册接口的实例
可以直接让其中一个接口的实例作为另一个接口的解析来源,减少重复注册逻辑:
using IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services .AddScoped<IScopedOperation1, DefaultOperation>() .AddScoped<IScopedOperation2>(sp => sp.GetRequiredService<IScopedOperation1>()) .AddTransient<OperationLogger>()) .Build(); host.Services.GetService<OperationLogger>().LogOperations();
原理
默认情况下,每个AddScoped<TInterface, TImplementation>会让DI容器为该接口维护独立的Scoped实例缓存。通过让多个接口指向同一个实例(无论是直接引用实现类的注册,还是复用其他接口的实例),就能保证同一作用域内所有接口请求都拿到同一个对象。
内容的提问来源于stack exchange,提问作者drazen
相关产品推荐
相关产品推荐

