ASP.NET Core 如何使用 DI 注入特定接口实现

How to inject specific interface implementation using DI in ASP NET Core

我有以下问题。我有一个由 2 类 实现的接口。其中一个 类 完成实际工作,而另一个使用提到的第一个。 在实例化一种类型的对象时,如何告诉框架使用接口的特定实现? 我希望控制器获得外观实现而不是真正的实现:

public interface IDependency
{
   void Method();
}

public class Real : IDependency
{
   public void Method()
   {
   }
}

public class Facade : IDependency
{
   private IDependency dependency;
   Facade(IDependency dependency)  //I want a Real here
   {
     this.dependency=dependency;
   }
   public void Method()=>dependency.Method();
}


public MyController : Controller
{
   private IDependency dependency;
   MyController(IDependency dependency)   //I want the `Facade` here not the `Real`
   {
      this.dependency=dependency;
   }
   
   [HttpGet]
   [Route("[some route]")]
   public async Task CallMethod()
   {
      await this.dependency.Method();
   }

}

正如你在上面的例子中看到的,我需要一个 Real 类型的 IDependency 注入到我的 Facade 中,而我需要一个 Facade 类型的IDependency 注入了我的 MyController。 这怎么可能?

Microsoft.Extensions.DependencyInjection 没有办法做到这一点。你所能做的就是注入 all 实现,即通过 List<IDependency>。然后,您可以实现您想要选择的任何手动逻辑,并从那里的可用选项中进行选择。

如果您对这些 class 有任何控制权,最好简单地实现一个不同的接口,以区分两者。例如,您可以创建一个继承自 IDependencyIFacadeDependency 接口,然后让 Facade class 实现它。然后,你可以注入 IFacadeDependency 并得到你想要的。

按如下方式注册您的依赖项:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddTransient<IDependency>(_ => new Facade(new Real()));
}

如果您有其他控制器需要不同的 IDependency 实现,您将希望将您的控制器注册为服务,从而允许覆盖注册。例如,如果您希望大多数控制器将 IDependency 解析为 Real,但只有 MyController 将 IDependency 解析为 Facade,您可以这样做:

    public void ConfigureServices(IServiceCollection services)
    {
        // Adds controllers as services, allowing their registrations to be overwritten.
        services.AddMvc().AddControllersAsServices();  

        //services.AddControllers();  REMOVE THIS

        // Makes Real the default implementation of IDependency
        services.AddTransient<IDependency, Real>();               

        // Overwrite the default registration of MyController to instantiate using Facade.
        services.AddTransient<MyController>(sp => 
            new MyController(new Facade(sp.GetService<IDependency>())));
    }