.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

