ASP.NET Core 提供了一个内置的依赖注入框架,您可以利用它来注册和访问服务。 我之前的一篇文章解释了在注册服务时可以使用的各种生命周期选项。 一旦服务注册到 DI 容器,您就可以使用所谓的构造函数注入获取服务的实例。 尽管构造函数注入是访问已注册服务的最常见方式,但还有一些其他选项可用于完成此任务。 为此,本文通过一个简单示例讨论了这些备选方案。
作为示例,我们将创建一个名为 NotificationHelper
的简单类,该类基于 INotificationHelper
接口。 它们如下所示:
public interface INotificationHelper
{
void Notify();
}
public class NotificationHelper:INotificationHelper
{
public void Notify()
{
//do something here
}
}
上面显示的代码非常简单。 INotificationHelper
的实际实现对于本文的主题并不重要。 所以,我们会让事情变得非常简单。
现在,NotificationHelper
已使用 AddScoped()
方法注册到 DI 容器。 此代码将进入 Startup
的 ConfigureServices()
方法,如下所示:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddScoped<INotificationHelper,NotificationHelper>();
}
一旦我们的 INotificationHelper
注册到 DI 框架,我们就可以使用下面讨论的方法在我们的控制器中访问它。
构造函数注入
构造函数注入可能是访问控制器内 DI 服务的最常见方式。 顾名思义,构造函数注入方法是在控制器的构造函数中注入一个服务实例。 然后您可以将该对象存储在一个成员变量中,以便可以在控制器的任何操作中访问该服务。
以下代码显示了 INotificationHelper
被注入到 HomeController
的构造函数中:
public class HomeController : Controller
{
private INotificationHelper helper = null;
public HomeController(INotificationHelper helper)
{
this.helper = helper;
}
}
如您所见,构造函数采用 INotificationHelper
类型的参数。 在运行时,DI 框架将创建所需的实例并将其提供给构造函数。 在内部,您将对 INotificationHelper
注入对象的引用保存到私有成员变量 - helper 中。
现在您可以在任何操作中使用辅助对象,如下所示:
public IActionResult Index()
{
helper.Notify();
return View();
}
Action 注入
当许多操作都需要服务时,构造函数注入效果很好。 有时您只需要在一两个动作中获得特定服务。 在这种情况下,您可以将服务注入到正在考虑的那些操作中,而不是在控制器中注入服务。 [FromServices]
属性促进了动作注入。 下面的代码会让[FromServices]
的使用一目了然。
public IActionResult Index([FromServices]INotificationHelper helper)
{
helper.Notify();
return View();
}
上面的代码显示了修改后的 Index()
操作。 这次 Index()
操作采用 INotificationHelper
参数。 更重要的是,这个参数是用 [FromServices]
属性修饰的。 现在,DI 框架会在调用它时将服务实例直接注入到 Index()
操作中。
注入 IServiceProvider
想象一下您需要将许多 DI 服务注入控制器的情况。 显然你的控制器需要有那么多参数。 此外,在某些情况下,您可能事先不知道在运行时需要哪种 DI 服务。 在这种情况下,您可以将 IServiceProvider
注入控制器。 IServiceProvider
允许您在运行时访问任何已注册的 DI 服务。 下面的代码会很清楚。
public class HomeController : Controller
{
private IServiceProvider provider = null;
public HomeController(IServiceProvider provider)
{
this.provider = provider;
}
}
在这种情况下,构造函数采用 IServiceProvider
的参数并将注入的实例存储到成员变量 - provider
中。 您现在可以在任何操作中使用提供者对象。 如下所示:
public IActionResult Index()
{
INotificationHelper helper = (INotificationHelper)
provider.GetService(typeof(INotificationHelper));
helper.Notify();
return View();
}
如您所见,代码调用了 IServiceProvider
的 GetService()
方法来获取服务实例。 GetService()
接受正在访问的服务类型 - INotificationHelper
。 GetService()
返回普通对象,因此您需要将其类型转换为 INotificationHelper
。
尽管您可以如上所示使用 IServiceProvider
,但请尽量避免直接使用它,除非有充分的理由这样做。 尽可能在构造函数(或操作)中注入显式服务类型。
使用 HttpContext.RequestServices
在前面的示例中,我们将 IServiceProvider
注入到构造函数中。 但有一个捷径——您可以使用 HttpContext
的 RequestServices
属性获取 IServiceProvider
。 以下代码显示了这是如何完成的。
public IActionResult Index()
{
INotificationHelper helper= (INotificationHelper)
HttpContext.RequestServices.GetService
(typeof(INotificationHelper));
return View();
}
该代码现在使用通过 HttpContext
的 RequestServices
属性公开的 IServiceProvider
并对其调用 GetService()
方法。
与以前的方法一样,这种方法也应该谨慎使用。