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

.NET Core中多个Rx Subject<Foo>的依赖注入方案咨询

Alright, let's figure out how to get those two distinct Subject<Foo> instances properly injected in your .NET Core app. The goal is to ensure Service A/B receive the "awesome" instance while Service C/D get the "great" one—here are three solid approaches using the built-in DI system:

1. Keyed Services (.NET 6+)

If you're using .NET 6 or later, the official recommended way for this scenario is keyed services. This lets you register multiple instances of the same type with unique keys, then resolve them by that key.

Registration (Program.cs/Startup.cs)

// Register the two distinct Subject<Foo> instances with unique keys
builder.Services.AddKeyedSingleton<Subject<Foo>>("awesome");
builder.Services.AddKeyedSingleton<Subject<Foo>>("great");

// Register your dependent services, resolving the correct keyed instance
builder.Services.AddScoped<ServiceA>(sp => 
    new ServiceA(sp.GetRequiredKeyedService<Subject<Foo>>("awesome")));
builder.Services.AddScoped<ServiceB>(sp => 
    new ServiceB(sp.GetRequiredKeyedService<Subject<Foo>>("awesome")));

builder.Services.AddScoped<ServiceC>(sp => 
    new ServiceC(sp.GetRequiredKeyedService<Subject<Foo>>("great")));
builder.Services.AddScoped<ServiceD>(sp => 
    new ServiceD(sp.GetRequiredKeyedService<Subject<Foo>>("great")));

Or Use Constructor Attribute Injection

For cleaner code, you can use the [FromKeyedServices] attribute directly in your service constructors instead of manual factory registration:

public class ServiceA
{
    private readonly Subject<Foo> _awesomeSubject;

    // DI will automatically resolve the "awesome" keyed instance
    public ServiceA([FromKeyedServices("awesome")] Subject<Foo> awesomeSubject)
    {
        _awesomeSubject = awesomeSubject;
    }
}

2. Marker Interfaces (Compatible with Older .NET Versions)

If you're on .NET 5 or earlier (or prefer more explicit semantic types), create marker interfaces to differentiate the two subjects. This makes your dependencies more readable and avoids string-based keys.

Step 1: Define Marker Interfaces

// Marker interfaces that inherit from ISubject<Foo> (assuming Subject implements this)
public interface IAwesomeSubject : ISubject<Foo> { }
public interface IGreatSubject : ISubject<Foo> { }

Step 2: Register the Instances

Since Subject<Foo> is likely a third-party type (e.g., Rx.NET), you can either wrap it in a concrete class or register it directly against the marker interface:

// Option 1: Wrap Subject<Foo> (if it's sealed or you need custom logic)
public class AwesomeSubject : Subject<Foo>, IAwesomeSubject { }
public class GreatSubject : Subject<Foo>, IGreatSubject { }

builder.Services.AddSingleton<IAwesomeSubject, AwesomeSubject>();
builder.Services.AddSingleton<IGreatSubject, GreatSubject>();

// Option 2: Direct registration (if Subject<Foo> can be instantiated directly)
builder.Services.AddSingleton<IAwesomeSubject>(_ => new Subject<Foo>());
builder.Services.AddSingleton<IGreatSubject>(_ => new Subject<Foo>());

Step 3: Inject the Marker Interfaces

Now your services can depend on the explicit interface instead of the generic Subject<Foo>:

public class ServiceA
{
    private readonly IAwesomeSubject _subject;

    public ServiceA(IAwesomeSubject subject)
    {
        _subject = subject;
    }
}

public class ServiceC
{
    private readonly IGreatSubject _subject;

    public ServiceC(IGreatSubject subject)
    {
        _subject = subject;
    }
}

3. Factory Class (For Complex Initialization)

If you need more control over how the Subject<Foo> instances are created (e.g., adding initialization logic, dynamic configuration), a factory class is a great option.

Step 1: Create a Factory Interface and Implementation

public interface ISubjectFactory
{
    Subject<Foo> GetAwesomeSubject();
    Subject<Foo> GetGreatSubject();
}

public class SubjectFactory : ISubjectFactory
{
    // Hold the singleton instances internally
    private readonly Subject<Foo> _awesomeSubject;
    private readonly Subject<Foo> _greatSubject;

    public SubjectFactory()
    {
        _awesomeSubject = new Subject<Foo>();
        _greatSubject = new Subject<Foo>();
        
        // Add any initialization logic here (e.g., default subscriptions, configuration)
    }

    public Subject<Foo> GetAwesomeSubject() => _awesomeSubject;
    public Subject<Foo> GetGreatSubject() => _greatSubject;
}

Step 2: Register the Factory

builder.Services.AddSingleton<ISubjectFactory, SubjectFactory>();

Step 3: Inject the Factory into Services

public class ServiceA
{
    private readonly Subject<Foo> _awesomeSubject;

    public ServiceA(ISubjectFactory factory)
    {
        _awesomeSubject = factory.GetAwesomeSubject();
    }
}

public class ServiceC
{
    private readonly Subject<Foo> _greatSubject;

    public ServiceC(ISubjectFactory factory)
    {
        _greatSubject = factory.GetGreatSubject();
    }
}

Which Approach Should You Choose?

  • Keyed Services: Best for .NET 6+—clean, built-in, and avoids extra types.
  • Marker Interfaces: Great for older .NET versions or when you want explicit, self-documenting dependencies.
  • Factory Class: Ideal when you need custom initialization or dynamic control over instance creation.

内容的提问来源于stack exchange,提问作者Nicolas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:56:21