You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 07:30:51