范围/瞬态依赖注入确保返回接口和实现的相同实例

mon*_*nty 4 c# dependency-injection .net-core asp.net-core

请随意为问题建议一个更好的标题。我想不出一个好名字来描述这个问题。

我需要在启动时通过依赖注入来访问一个类。该类不仅应该通过它的具体实现来使用,还应该通过它实现的接口来使用。我需要确保它是通过两次注入返回的同一对象实例。

导致我想到单例案例的现实世界场景是提供接口的抽象块(IStore),多个块保存具体实现(DBStore、RedisStore)。当我尝试对每个存储实现进行运行状况检查时,我可以注入 IStore,但不能注入具体实现。我想使用一些在具体实现中初始化和修改的变量(这就是为什么我两次注入都需要相同的实例)。由于商店(希望)被用作单身人士,所以它起作用了。我并不是说存在作用域和瞬态方式的现实场景。我只是好奇如果他们不是单身人士这是否可能。

以下代码描述了我如何设法使用单例来做到这一点。

让我找到单例解决方案的方式:

有这个界面:

public interface ITestInterface
{
  string ReturnAString();
  int ReturnAnInt(); 
}
Run Code Online (Sandbox Code Playgroud)

以及这个具体的实现

public class TestImplementation : ITestInterface
{
  private int counter = 0;
  public string ReturnAString() {return "a string"; }
  public int ReturnAnInt() { return counter++; }
}
Run Code Online (Sandbox Code Playgroud)

它们用于两个(比方说)服务。一种需要在构造函数中注入接口,另一种需要具体的实现。

Startup.ConfigureServices 方法中用于在两种情况下注入相同实例的尝试和错误:

尝试1:

// only ITestInterface is injected but not TestImplemenation
services.AddSingleton<ITestInterface, TestImplementation>();
Run Code Online (Sandbox Code Playgroud)

尝试2:

//only TestImplementation is injected (DI does not recognize it implements the Interface)
services.AddSingleton<TestImplementation>();
Run Code Online (Sandbox Code Playgroud)

尝试3:

// both are injected but they are not singleton any more (counters increment independently)
services.AddSingleton<ITestInterface, TestImplementation>();
services.AddSingleton<TestImplementation, TestImplementation>();
Run Code Online (Sandbox Code Playgroud)

尝试4:

TestImplementation instance = new TestImplementation();
services.AddSingleton<ITestInterface>(instance);
services.AddSingleton(instance);
//services.AddSingleton<TestImplementation>(instance);
Run Code Online (Sandbox Code Playgroud)

好吧,在尝试 4 时,我的两次注射都有相同的实例。

现在假设 TestImplementation 需要注入一些(例如连接)设置。

我可以编写一个扩展方法来从配置中获取设置并将其传递给单例实例。

TestImplementation instance = new TestImplementation(Configuration.GetTestSettings());
services.AddSingleton<ITestInterface>(instance);
services.AddSingleton(instance);
Run Code Online (Sandbox Code Playgroud)

那么,我如何实现两个注入都是使用相同范围或瞬态的相同设置的同一实例?因为我不认为我可以在那里手动/代码创建实例。

小智 5

基本上,您希望将单个服务实现类型注册为两个服务契约(具体类+接口)。这是很常见的情况,但不幸的是默认的 Microsoft DI 容器 (ServiceCollection) 的功能有限,我认为实现所需效果的唯一方法是使用工厂委托:

services.AddScoped<TestImplementation>();
services.AddScoped<ITestInterface>(s => s.GetRequiredService<TestImplementation>());
Run Code Online (Sandbox Code Playgroud)

虽然这可以解决问题(需要一些额外的运行时成本),但我强烈建议使用功能齐全的 DI 容器之一,例如 Autofac 或 Ninject