构建 ASP.NET Core 应用程序的时候, 依赖注入已成为了. NET Core 的核心, 这篇文章, 我们理一理依赖注入的使用方法.
不使用依赖注入
首先, 我们创建一个 ASP.NET Core Mvc 项目, 定义个表达的爱服务接口, 中国小伙类实现这个类如下:
- public interface ISayLoveService
- {
- string SayLove();
- }
- public class CNBoyService : ISayLoveService
- {
- public string SayLove()
- {
- return "安红, 我喜欢你";
- }
- }
在 LoveController 控制器中调用 ISayLoveService 的 SayLove 方法.
- public class LoveController : Controller
- {
- private ISayLoveService loveService;
- public IActionResult Index()
- {
- loveService = new CNBoyService(); // 中国小伙对安红的表达
- ViewData["SayLove"] = loveService.SayLove();
- return View();
- }
- }
输出如图:
小结: LoveController 控制器调用 ISayLoveService 服务的 SayLove 方法; 我们的做法, 直接在控制器去 new CNBoyService() 实例对象, 也就是 LoveController 依赖 ISayLoveService 类.
思考: 能不能有种模式, new 实例不要在使用的时候进行创建, 而是在外部或者有一个容器进行管理; 这不就是 IoC 思想吗? 好处, 代码的解耦, 代码更好的维护等等.
使用依赖注入
上面的疑惑, 答案是肯定的, 有! 并且 ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式 (当然也可以兼容第三方). 我们还使用上面的代码,
服务注册
在 Startup 类 ConfigureServices 方法中注册服务容器中的依赖关系
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddSingleton<ISayLoveService, CNBoyService>();
- services.AddControllersWithViews();
- }
在 LoveControlle 控制器中, 通过构造函数注入
- private readonly ISayLoveService loveService;
- public LoveController(ISayLoveService loveService)
- {
- this.loveService = loveService;
- }
- public IActionResult Index()
- {
- ViewData["SayLove"] = loveService.SayLove();
- return View();
- }
LoveController 正在将 ISayLoveService 作为依赖项注入其构造函数中, 然后在 Index 方法中使用它.
推荐:
将注入的依赖项分配给只读字段 / 属性 (以防止在方法内部意外为其分配另一个值).
使用接口或基类抽象化依赖关系实现.
小结: 在控制器中, 还有几种使用如:[FromServices] 标签 , HttpContext.RequestServices.GetService<T>(); 我们发现可以使用 ASP.NET Core 提供了一个内置的服务容器 IServiceProvider. 服务只需要在 Startup.ConfigureServices 方法中注册, 然后在运行时将服务注入 到使用它的类的构造函数中. 框架负责创建依赖关系的实例, 并在不再需要时对其进行处理.
思考: 服务注册的时候使用的是 AddSingleton, 如 services.AddSingleton<ISayLoveService, CNBoyService>(); 还有其他的吗?
服务生命周期
服务注册的时候, ASP.NET Core 支持指定三种生命周期如:
Singleton 单例
Scoped 范围
Transient 短暂的
Singleton 仅创建一个实例. 该实例在需要它的所有组件之间共享. 因此始终使用同一实例.
Scoped 每个范围创建一个实例. 在对应用程序的每个请求上都会创建一个范围, 因此每个请求将创建一次注册为 Scoped 的任何组件.
Transient 在每次被请求时都会创建, 并且永不共享.
为了能够更好的裂解生命周期的概念, 我们把上面代码稍作改动, 做一个测试:
ISayLoveService 新增个属性 LoveId, 类型为 guid,
- public interface ISayLoveService
- {
- Guid LoveId { get; }
- string SayLove();
- }
- public interface ITransientSayLoveService : ISayLoveService
- {
- }
- public interface IScopedSayLoveService : ISayLoveService
- {
- }
- public interface ISingletonSayLoveService : ISayLoveService
- {
- }
- public interface ISingletonInstanceSayLoveService : ISayLoveService
- {
- }
BoyService 也很简单, 在构造函数中传入一个 Guid, 并对它进行赋值.
- public class BoyService : ITransientSayLoveService,
- IScopedSayLoveService,
- ISingletonSayLoveService,
- ISingletonInstanceSayLoveService
- {
- public BoyService():this(Guid.NewGuid()) { }
- public BoyService(Guid id)
- {
- LoveId = id;
- }
- public Guid LoveId { get; private set; }
- public string SayLove()
- {
- return LoveId.ToString();
- }
- }
每个实现类的构造函数中, 我们都产生了一个新的 guid, 通过这个 GUID, 我们可以判断这个类到底重新执行过构造函数没有.
服务注册代码如下:
- public void ConfigureServices(IServiceCollection services)
- {
- // 生命周期设置为 Transient, 因此每次都会创建一个新实例.
- services.AddTransient<ITransientSayLoveService, BoyService>();
- services.AddScoped<IScopedSayLoveService, BoyService>();
- services.AddSingleton<ISingletonSayLoveService, BoyService>();
- services.AddSingleton<ISingletonInstanceSayLoveService>(new BoyService(Guid.Empty));
- services.AddControllersWithViews();
- }
在 LifeIndex 方法中多次调用 ServiceProvider 的 GetService 方法, 获取到的都是同一个实例.
- public IActionResult LifeIndex()
- {
- ViewData["TransientSayLove1"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
- ViewData["ScopedSayLove1"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
- ViewData["SingletonSayLove1"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
- ViewData["SingletonInstanceSayLove1"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove();
- // 同一个 HTTP 请求 , 在从容器中获取一次
- ViewData["TransientSayLove2"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
- ViewData["ScopedSayLove2"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
- ViewData["SingletonSayLove2"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
- ViewData["SingletonInstanceSayLove2"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove();
- return View();
- }
我们编写 view 页面, 来展示这些信息如下:
- @{
- ViewData["Title"] = "LifeIndex";
- }
- <div class="row">
- <div class="panel panel-default">
- <div class="panel-heading">
- <h2 class="panel-title">Operations</h2>
- </div>
- <div class="panel-body">
- <h3 > 获取第一次 </h3>
- <dl>
- <dt>Transient1</dt>
- <dd>@ViewData["TransientSayLove1"] </dd>
- <dt>Scoped1</dt>
- <dd>@ViewData["ScopedSayLove1"]</dd>
- <dt>Singleton1</dt>
- <dd>@ViewData["SingletonSayLove1"] </dd>
- <dt>Instance1</dt>
- <dd>@ViewData["SingletonInstanceSayLove1"]</dd>
- </dl>
- <h3 > 获取第二次 </h3>
- <dl>
- <dt>Transient2</dt>
- <dd>@ViewData["TransientSayLove2"]</dd>
- <dt>Scoped2</dt>
- <dd>@ViewData["ScopedSayLove2"]</dd>
- <dt>Singleton2</dt>
- <dd>@ViewData["SingletonSayLove2"]</dd>
- <dt>Instance2</dt>
- <dd>@ViewData["SingletonInstanceSayLove2"]</dd>
- </dl>
- </div>
- </div>
- </div>
运行代码第一次输出:
我们发现, 在一次请求中, 发现单例, 范围的生命周期的 guid 没有变化, 说明分别用的是同一个对象, 而瞬态 guid 不同, 说明对象不是一个.
刷新之后, 查看运行效果
我们发现通过刷新之后, 单例模式的 guid 还是跟首次看到的一样, 其他的都不同;
总结: 如果您将组件 A 注册为单例, 则它不能依赖已注册 "作用域" 或 "瞬态" 生存期的组件. 一般而言: 组件不能依赖寿命短于其寿命的组件. 如果默认的 DI 容器不能满足项目需求, 可以替换成第三方的如功能强大的 Autofac.
来源: https://www.cnblogs.com/chengtian/p/11769338.html