如何用Simple Injector注入特定IConnector实现并实现条件注册
Hey there! Let's walk through how to set up this conditional dependency injection properly, step by step. I'll focus on Microsoft.Extensions.DependencyInjection first (since it's the default for most .NET projects), and also touch on Autofac in case you're using that.
First, Let's Confirm the Class Structure
Just to align on what we're working with, here's the core type setup based on your description:
// Your base interfaces and implementations public interface IConnector { } public class SomeConnector : IConnector { // Depends on its own exclusive context public SomeConnector(IConnectorContext context) { // Initialize using context properties like Types/DataTimeoutSeconds } } public interface IConnectorContext { } public class SomeConnectorContext : IConnectorContext { public string Types { get; set; } public int DataTimeoutSeconds { get; set; } } // Worker classes with specific dependencies public class Worker1 { // Needs two distinct IConnector instances (each with their own context) public Worker1(IConnector connector1, IConnector connector2) { } } public class Worker2 { // Needs one IConnector with its specific context public Worker2(IConnector connector) { } }
Step 1: Implement Conditional Registration Logic
The key here is branching based on your workerType config value. We'll use a switch statement to keep things clean, and leverage named services when we need multiple instances of the same interface (like two IConnectors for Worker1).
For Microsoft.Extensions.DependencyInjection:
var services = new ServiceCollection(); var workerType = Properties.Settings.Default.WorkerType; switch (workerType) { case "worker1": // Register first Connector + its exclusive context services.AddSingleton<IConnectorContext>(_ => new SomeConnectorContext { Types = "Connector1Type", // Adjust these values to your needs DataTimeoutSeconds = 30 }); services.AddSingleton<IConnector>(sp => new SomeConnector(sp.GetRequiredService<IConnectorContext>())); // Register second Connector + its separate context (use a named service to avoid conflict) services.AddSingleton<IConnectorContext>("Worker1Connector2Context", _ => new SomeConnectorContext { Types = "Connector2Type", DataTimeoutSeconds = 60 }); services.AddSingleton<IConnector>("Worker1Connector2", sp => new SomeConnector(sp.GetRequiredService<IConnectorContext>("Worker1Connector2Context"))); // Register Worker1, explicitly injecting both named Connectors services.AddSingleton<Worker1>(sp => new Worker1( sp.GetRequiredService<IConnector>(), // Uses the first (unnamed) Connector sp.GetRequiredService<IConnector>("Worker1Connector2") )); break; case "worker2": // Register Worker2's single Connector + context services.AddSingleton<IConnectorContext>(_ => new SomeConnectorContext { Types = "Worker2ConnectorType", DataTimeoutSeconds = 45 }); services.AddSingleton<IConnector, SomeConnector>(); services.AddSingleton<Worker2>(); break; default: throw new InvalidOperationException($"Unsupported worker type: {workerType}"); }
If You're Using Autofac:
If your project uses Autofac instead, the logic is similar—just with Autofac's registration syntax:
var builder = new ContainerBuilder(); var workerType = Properties.Settings.Default.WorkerType; switch (workerType) { case "worker1": // First Connector + context builder.Register(_ => new SomeConnectorContext { Types = "Connector1Type", DataTimeoutSeconds = 30 }).As<IConnectorContext>().SingleInstance(); builder.RegisterType<SomeConnector>() .Named<IConnector>("Worker1Connector1") .SingleInstance(); // Second Connector + its unique context (named to avoid overlap) builder.Register(_ => new SomeConnectorContext { Types = "Connector2Type", DataTimeoutSeconds = 60 }).Named<IConnectorContext>("Worker1Connector2Context").SingleInstance(); builder.Register(ctx => new SomeConnector( ctx.ResolveNamed<IConnectorContext>("Worker1Connector2Context"))) .Named<IConnector>("Worker1Connector2") .SingleInstance(); // Register Worker1 with both named Connectors builder.Register(ctx => new Worker1( ctx.ResolveNamed<IConnector>("Worker1Connector1"), ctx.ResolveNamed<IConnector>("Worker1Connector2"))) .AsSelf().SingleInstance(); break; case "worker2": builder.Register(_ => new SomeConnectorContext { Types = "Worker2ConnectorType", DataTimeoutSeconds = 45 }).As<IConnectorContext>().SingleInstance(); builder.RegisterType<SomeConnector>().As<IConnector>().SingleInstance(); builder.RegisterType<Worker2>().AsSelf().SingleInstance(); break; default: throw new InvalidOperationException($"Unsupported worker type: {workerType}"); } var container = builder.Build();
Key Things to Note
- Named Services: When you need multiple instances of the same interface (like two
IConnectors for Worker1), named services let you distinguish between them so the DI container knows which one to inject where. - Exclusive Contexts: Each
SomeConnectorgets its own uniqueSomeConnectorContextinstance—we never reuse a context between connectors unless you specifically want that (which you said you don't!). - Clean Conditional Logic: Using a switch statement makes it easy to add new worker types later without cluttering up your registration code.
That's it! This setup will handle your conditional registration needs perfectly, ensuring each Worker gets exactly the Connectors and contexts it requires based on your workerType config.
内容的提问来源于stack exchange,提问作者shda

