ASP.NET Core 依赖注入

ASP.NET Core 从头开始设计,以支持依赖注入。ASP.NET Core 使用内置 IoC 容器通过构造函数或方法注入依赖类的对象。

内置 IoC 容器

ASP.NET Core 框架包含简单的开箱即用 IoC 容器,它不像其他第三方 IoC 容器那样具有许多功能。如果您需要更多功能,如自动注册、扫描、拦截器或装饰器,那么您可以用第三方容器替换内置 IoC 容器。

默认情况下,内置容器由支持构造函数注入的 IServiceProvider 实现表示。内置 IoC 容器管理的类型(类)称为服务。

ASP.NET Core 中基本上有两种类型的服务:

  • 框架服务:作为 ASP.NET Core 框架,如 IApplicationBuilderIHostingEnvironmentILoggerFactory 等。
  • 应用程序服务:程序员为应用程序创建的服务(自定义类型或类)。

为了让 IoC 容器自动注入我们的应用程序服务,我们首先需要向 IoC 容器注册它们。


注册应用程序服务

考虑以下简单 ILog 接口及其实现类的示例。我们将看到如何在内置 IoC 容器中注册它,并在我们的应用程序中使用它。

  1. public interface ILog
  2. {
  3. void info(string str);
  4. }
  5. class MyConsoleLogger : ILog
  6. {
  7. public void info(string str)
  8. {
  9. Console.WriteLine(str);
  10. }
  11. }

ASP.NET Core 允许我们在 Startup 类的 ConfigureServices 方法中向 IoC 容器注册应用程序服务。ConfigureServices 方法包含 IServiceCollection 类型的参数,该参数用于注册应用程序服务。

让我们在 ConfigureServices() 方法中使用 IoC 容器注册ILog,如下所示。

  1. public class Startup
  2. {
  3. public void ConfigureServices(IServiceCollection services)
  4. {
  5. services.Add(new ServiceDescriptor(typeof(ILog), new MyConsoleLogger()));
  6. }
  7. // other code removed for clarity...
  8. }

如上所述,IServiceCollection 实例的 Add() 方法用于向 IoC 容器注册服务。ServiceDescriptor 用于指定服务类型及其实例。我们将 ILog 指定为服务类型,将 MyConsoleLogger 指定为其实例。这将在默认情况下将 ILog 服务注册为单例。现在,IoC 容器将创建 MyConsoleLogger 类的单例对象,并将其注入到类的构造函数中,无论我们在整个应用程序中将 ILog 作为构造函数或方法参数。

因此,我们可以使用 ASP.NET Core 应用程序中的 IoC 容器注册我们的自定义应用程序服务。还有其他扩展方法可用于快速方便地注册服务,我们将在本章稍后介绍。


了解服务的生命周期

内置 IoC 容器管理已注册服务类型的生命周期。它会根据指定的生命周期自动配置服务实例。

内置 IoC 容器支持三种生命周期:

  • Singleton - IoC 容器将在应用程序的整个生命周期中创建和共享服务的单个实例。
  • Transient - 每次您请求时,IoC 容器都会创建指定服务类型的新实例。
  • ScopedIoC 容器将为每个请求创建一次指定服务类型的实例,并将在单个请求中共享。

下面的实例显示了如何注册具有不同生命周期的服务。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.Add(new ServiceDescriptor(typeof(ILog), new MyConsoleLogger())); // singleton
  4. services.Add(new ServiceDescriptor(typeof(ILog), typeof(MyConsoleLogger), ServiceLifetime.Transient)); // Transient
  5. services.Add(new ServiceDescriptor(typeof(ILog), typeof(MyConsoleLogger), ServiceLifetime.Scoped)); // Scoped
  6. }

注册的扩展方法

ASP.NET Core 框架包括每种生存期类型的扩展方法;AddSingleton()AddTransient()AddScoped() 方法分别用于 singletontransientscoped 生命周期。

以下实例显示了使用扩展方法注册类型(服务)的方法。

  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddSingleton<ILog, MyConsoleLogger>();
  4. services.AddSingleton(typeof(ILog), typeof(MyConsoleLogger));
  5. services.AddTransient<ILog, MyConsoleLogger>();
  6. services.AddTransient(typeof(ILog), typeof(MyConsoleLogger));
  7. services.AddScoped<ILog, MyConsoleLogger>();
  8. services.AddScoped(typeof(ILog), typeof(MyConsoleLogger));
  9. }

构造器注入

注册服务后,如果服务类型作为参数包含在构造函数中,IoC 容器将自动执行构造函数注入。

例如,我们可以在任何 MVC 控制器中使用 ILog 服务类型。考虑以下实例。

  1. public class HomeController : Controller
  2. {
  3. ILog _log;
  4. public HomeController(ILog log)
  5. {
  6. _log = log;
  7. }
  8. public IActionResult Index()
  9. {
  10. _log.info("Executing /home/index");
  11. return View();
  12. }
  13. }

在上面的实例中,IoC 容器将自动将 MyConsoleLogger 的实例传递给 HomeController 的构造函数。我们不需要做任何其他事情。IoC 容器将根据注册的生存期创建和处置 ILog 实例。


Action 方法注入

有时,我们可能只需要在单个操作方法中使用依赖服务类型。为此,在方法中使用 [FromServices] 属性和服务类型参数。

  1. using Microsoft.AspNetCore.Mvc;
  2. public class HomeController : Controller
  3. {
  4. public HomeController()
  5. {
  6. }
  7. public IActionResult Index([FromServices] ILog log)
  8. {
  9. log.info("Index method executing");
  10. return View();
  11. }
  12. }

属性注入

内置 IoC 容器不支持属性注入。您必须使用第三方 IoC 容器。


手动获取服务

不需要在构造函数中包含依赖项服务。我们可以使用 HttpContextRequestServices 属性手动访问使用内置 IoC 容器配置的依赖服务,如下所示。

  1. public class HomeController : Controller
  2. {
  3. public HomeController()
  4. {
  5. }
  6. public IActionResult Index()
  7. {
  8. var services = this.HttpContext.RequestServices;
  9. var log = (ILog)services.GetService(typeof(ILog));
  10. log.info("Index method executing");
  11. return View();
  12. }
  13. }

建议使用构造函数注入,而不是使用 RequestServices 获取它。