限定作用域/瞬时依赖注入,确保接口和实现返回相同的实例。

4
请随意建议一个更好的问题标题。我无法想出一个好的名称来描述这个问题。
我需要通过依赖注入,使一个类在启动时可访问。该类不仅应该通过其具体实现而还可以通过它实现的接口进行访问。并且我需要确保两次注入返回的是同一个对象实例。
导致我使用单例模式的真实场景是一个抽象 nuget 提供了接口(IStore),多个 nuget 拥有具体实现(DBStore、RedisStore)。当我尝试为每个存储实现实现健康检查时,我可以获得 IStore 的注入,但不能获取具体实现的注入。而且我希望使用一些在具体实现中初始化和修改的变量(这就是为什么我需要两次注入返回同一个实例)。由于这些存储(希望)作为单例使用,所以它们起作用。我并不是说对于范围和瞬态方式有一个真正的场景。我只是很好奇如果它们不是单例,是否可能。
以下代码描述了我如何使用单例解决这个问题。
导致我使用单例解决方法的原因:
有了这个接口:
public interface ITestInterface
{
  string ReturnAString();
  int ReturnAnInt(); 
}

以及这个具体的实现

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

它们被用于两个(假设)服务中。一个服务需要在构造函数中注入接口,而另一个服务需要具体实现。

在Startup.ConfigureServices方法中尝试多次,并使两种情况下注入相同的实例:

尝试1:

// only ITestInterface is injected but not TestImplemenation
services.AddSingleton<ITestInterface, TestImplementation>();

尝试二:

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

尝试 3:

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

尝试4:

TestImplementation instance = new TestImplementation();
services.AddSingleton<ITestInterface>(instance);
services.AddSingleton(instance);
//services.AddSingleton<TestImplementation>(instance);

第四次尝试时,我为两种注入方式使用了相同的实例。

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

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

TestImplementation instance = new TestImplementation(Configuration.GetTestSettings());
services.AddSingleton<ITestInterface>(instance);
services.AddSingleton(instance);

那么我该如何在使用scoped或transient时,实现两次注入的是相同实例并使用相同设置呢?因为我认为我不能手动/编码创建实例。

我有类似的需求,然后我看到了Andre Lock的博客文章,其中描述了如何使用ASP.NET Core DI实现相同的功能。这是链接-https://andrewlock.net/how-to-register-a-service-with-multiple-interfaces-for-in-asp-net-core-di/ - tyrion
3个回答

5

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

services.AddScoped<TestImplementation>();
services.AddScoped<ITestInterface>(s => s.GetRequiredService<TestImplementation>());

虽然这样做可以解决问题(但会增加一些运行时成本),但我强烈建议使用Autofac或Ninject等功能齐全的DI容器。


1
如果我没记错的话,Autofac 可以通过使用 decorators 来实现你想要的功能。
然而,由于公司政策的限制,添加第三方库的依赖通常是不希望或甚至被禁止的。
在这种情况下,我认为最好创建一个工厂。例如:
public class TestFactory
{
    public ITestInterface Create(string flavor)
    {
        if (flavor == "concrete")
        {
            return new TestImplementation();
        }
        else
        {
            return new OtherTestImplementation();
        }
    }
}

然后,在您的 Startup.cs 中,您可以像这样注入它:

services.AddSingleton<TestFactory>();

请注意,最终,ITestInterface服务的生命周期将取决于您存储Create(...)方法调用结果的位置。

1
好的答案!请查看附加内容:https://dev59.com/1HE85IYBdhLWcg3wmEtW#2697810 - Shahar Shokrani

1
使用 Autofac,您可以使用 AsSelf() 方法同时添加接口和实现接口:
container.RegisterType<TestImplementation>.As<ITestInterface>().AsSelf();

查看此答案以获取更多解释。

在您的情况下 - 使用SingleInstance()作为单例模式。

container.RegisterType<TestImplementation>.As<ITestInterface>().AsSelf().SingleInstance();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接