在 ASP.NET Core 中使用一个接口的多个实现

如何在 ASP.NET Core 中向 IoC 容器注册接口的多个实现并在运行时检索特定服务

ASP.NET Core 中对依赖注入的内置支持非常棒。 但是,在 ASP.NET Core 中使用依赖注入时处理接口的多个实现有点棘手。 在本文中,我将向您展示如何从 ASP.NET Core 中的此类实现中动态选择服务。

在 Visual Studio 中创建 ASP.NET Core MVC 项目

首先,让我们在 Visual Studio 2019 中创建一个 ASP.NET Core Web 应用程序 MVC 项目。假设您的系统中安装了 Visual Studio 2019,请按照下面概述的步骤在 Visual Studio 中创建一个新的 ASP.NET Core MVC 项目。

  • 启动 Visual Studio IDE。
  • 单击“创建新项目”。
  • 在“创建新项目”窗口中,从显示的模板列表中选择“ASP.NET Core Web 应用程序”。
  • 点击下一步。
  • 在“配置新项目”窗口中,指定新项目的名称和位置。
  • 根据您的喜好,可选择选中“将解决方案和项目放在同一目录中”复选框。
  • 单击创建。
  • 在接下来显示的“创建新的 ASP.NET Core Web 应用程序”窗口中,选择 .NET Core 作为运行时,并从顶部的下拉列表中选择 ASP.NET Core 3.1(或更高版本)。
  • 选择“Web Application (Model-View-Controller)”作为项目模板,创建一个新的 ASP.NET Core MVC 应用程序。
  • 确保未选中“启用 Docker 支持”和“配置 HTTPS”复选框,因为我们不会在此处使用这些功能。
  • 确保身份验证设置为“无身份验证”,因为我们也不会使用身份验证。
  • 单击创建。

按照这些步骤应该在 Visual Studio 2019 中创建一个新的 ASP.NET Core MVC 项目。我们将在下面的部分中使用这个项目来说明我们如何在 ASP.NET Core 3.1 中注册一个接口的多个实现。

ASP.NET Core 中 IoC 容器的一个问题

ASP.NET Core 中内置的 IoC(控制反转)容器可能不像 AutoFacUnity 或其他一些流行的 IoC 容器那样广泛,但在大多数情况下你应该能够使用它来满足你的需求。 但是,内置的 IoC 容器不允许我们注册多个服务,然后在运行时检索特定的服务实例。

有一些 IoC 容器使您能够使用区分这些类型实例的唯一键来注册具体类型。 然而,ASP.NET Core 中内置的 IoC 容器缺乏对此的支持。 因此,注册具有公共接口的服务并在运行时解析它们并不简单。 让我们用一个例子来理解这一点。

假设您有一个名为 ICustomLogger 的接口,其代码如下:

public interface ICustomLogger
{
   public bool Write(string data);
}

ICustomLogger 接口由以下三个类实现:

public class FileLogger : ICustomLogger
{
   public bool Write(string data)
   {
       throw new System.NotImplementedException();
   }
}
public class DbLogger : ICustomLogger
{
   public bool Write(string data)
   {
       throw new System.NotImplementedException();
   }
}
public class EventLogger : ICustomLogger
{
  public bool Write(string data)
  {
      throw new System.NotImplementedException();
  }
}

FileLogger 类用于将数据记录到文件中,DbLogger 类用于将数据记录到数据库中,EventLogger 类用于将数据记录到事件日志中,以便可以使用事件查看器工具查看日志。 请注意,由于记录数据不是本文的目标,因此与这些类中的每一个相关的 Write 方法不包含任何实现。

现在假设您已经在 Startup 类的 ConfigureServices 方法中将这些类的实例添加为作用域服务,如下所示:

services.AddScoped<ICustomLogger, FileLogger>();
services.AddScoped<ICustomLogger, DbLogger>();
services.AddScoped<ICustomLogger, EventLogger>();

以下代码片段说明了如何在 HomeController 类的构造函数中利用依赖注入来使用这些服务。

public class HomeController : Controller
{
public HomeController(ICustomLogger fileLogger, ICustomLogger dbLogger, ICustomLogger eventLogger)
{
    var obj = fileLogger; //This code has been written for debugging
}
    //Action methods go here
}

When you run this application and the breakpoint is hit, you’ll observe that all three parameters are instances of type EventLogger because EventLogger has been injected last. This is shown in Figure 1 below.
当您运行此应用程序并命中断点时,您会发现所有三个参数都是 EventLogger 类型的实例,因为最后注入了 EventLogger。 这在下面的图 1 中显示。
在 ASP.NET Core 中使用一个接口的多个实现_第1张图片

在 ASP.NET Core 中对一个接口的多个实现使用依赖注入

我们如何克服 ASP.NET Core 中内置 IoC 容器的这种限制? 在下面概述的解决方案中,我们将使用 IEnumerable 服务集合来注册服务,并使用委托来检索特定服务实例。

下图还将包含示例代码,用于在控制器类的构造函数中使用依赖注入,解析服务实例,并根据运行时选择的服务类型返回三个类(FileLoggerDbLoggerEventLogger)的实例。

使用 IEnumerable 服务实例集合

ConfigureServices 方法中编写以下代码以添加每个 ICustomLogger 实现的作用域服务。

services.AddScoped<ICustomLogger, FileLogger>();
services.AddScoped<ICustomLogger, DbLogger>();
services.AddScoped<ICustomLogger, EventLogger>();

接下来,在控制器类的构造函数中利用依赖项注入和 ICustomLogger 实现的 IEnumerable 集合,如下面给出的代码片段所示。

public HomeController(IEnumerable<ICustomLogger> loggers)
{
   foreach(var logger in loggers)
   {
      var objType = logger.GetType();
   }
}

使用委托来检索特定的服务实例

请考虑以下枚举,其中包含对应于三种类型 FileLoggerDbLoggerEventLogger 的整数常量。

public enum ServiceType
{
   FileLogger,
   DbLogger,
   EventLogger
}

接下来,声明一个共享委托,如下面的代码片段所示。

public delegate ICustomLogger ServiceResolver(ServiceType serviceType);

现在将以下范围内的服务添加到服务集合实例中,如下一个代码片段所示。

services.AddScoped<FileLogger>();
services.AddScoped<DbLogger>();
services.AddScoped<EventLogger>();

在运行时根据服务类型返回实例

以下代码片段显示了如何根据运行时选择的服务类型返回 FileLoggerDbLoggerEventLogger 类的实例。

services.AddTransient<ServiceResolver>(serviceProvider => serviceTypeName =>
            {
                switch (serviceTypeName)
                {
                    case ServiceType.FileLogger:
                        return serviceProvider.GetService<FileLogger>();
                    case ServiceType.DbLogger:
                        return serviceProvider.GetService<DbLogger>();
                    case ServiceType.EventLogger:
                        return serviceProvider.GetService<EventLogger>();
                    default:
                        return null;
                }
            });

使用依赖注入并解析服务实例

最后,这里介绍了如何在控制器类的构造函数中使用依赖注入,然后解析服务实例。

public HomeController(Func<ServiceType, ICustomLogger> serviceResolver)
{
    var service = serviceResolver(ServiceType.FileLogger);
}

您也可以在运行时使用反射来解析类型,但这不是推荐的解决方案。 另一种可能的解决方案是在接口上使用泛型类型参数。

你可能感兴趣的:(ASP.NET,Core,Web,API,asp.net,后端,Net,core)