5 ASP.NET Core 依赖注入生命周期
通过依赖注入创建的服务有自己的生命周期,这里有两个问题
1 他们什么时候被创建
2 他们在内存里面保存多久之后才会被GC移除
服务有3种生命周期
1 Transient - 每次请求都会被创建
2 Scoped - 每次每个客户端请求被创建
3 Singleton - 只创建一次
6 依赖注入方法
我们常用三个方法来注册服务分别是Transient, Scoped & Singleton
6.1 AddTransient
AddTransient 用Transient方式创建的服务,当服务被请求时创建,这也意味着每次请求DI都会创建一个新的实例,我们在上面的例子使用了AddTransient()方式注入服务,每次在控制器中请求IRepository接口时,都会创建一个新的Repository.cs类对象
6.2 AddScoped
通过AddScoped方法注册的服务不像AddTransient 方法那样每次都被创建,实际上它复用了来自同一客户端的请求,这意味着,如果我们使用这个方法来代替AddTransient,那么Repository.cs类对象将只创建一个,并在浏览器的同一个请求后续中共享该实例,然而,如果我们从另一台电脑的浏览器请求该应用程序,那么(对于这个新客户端)将为每个后续请求创建并共享一个新服务
6.3 AddSingleton
AddSingleton方法仅仅在第一次请求时创建一个服务,它会在每个请求中复用,和AddScoped方法不一样,每个跨客户端的请求都会共享该服务
针对AddTransient, AddScoped & AddSingleton方法每个方法都有三种变体
1 <service, implType>() 这种变体为每个依赖项创建一个实现类型的实例,在上面我们已经使用过这中场景
2 <service>()这个变体注册单个类型的对象,我们前面也覆盖到了这种场景
3 <service>(factoryFunc)这种方式使用lambda表达式注册一个服务,我们可以添加我们逻辑,我们将在后面覆盖这种场景
7 通过例子来了解这三种类型
7.1 Transient服务的例子
Transient 服务必须在Program类中使用AddTransient方法注册,我们有2个Transient服务在我们应用程序中,他们是Repository和ProductSum
builder.Services.AddTransient<IRepository, Repository>();
builder.Services.AddTransient<ProductSum>();
ProductSum类在指定的构造函数指定了IRepository依赖
namespace AspNetCore.DependencyInjection.Models
{
public class ProductSum
{
public IRepository Repository { get; set; }
public ProductSum(IRepository repo)
{
Repository = repo;
}
public decimal Total => Repository.Products.Sum(p => p.Price);
}
}
HomeController指定针对这两个服务的依赖
public HomeController(IRepository repository, ProductSum productSum)
{
_repository = repository;
_productSum = productSum;
}
在Repository.cs类中重写ToString()方法,代码如下:
private string guid= Guid.NewGuid().ToString();
public override string ToString()
{
return guid;
}
我们可以通过这个值来判断依赖注入什么时候以及如何创建这个Repository,接下来进入HomeController并且在Index的方法中添加2个ViewBag变量,第一 ViewBag变量将包含从Repository接受的GUID,第二个ViewBag变量包含从ProductSum服务接受的GUID
public IActionResult Index()
{
ViewBag.HomeControllerGUID=_repository.ToString();
ViewBag.TotalGUID = _productSum.ToString();
return View(new Repository().Products);
}
现在运行应用程序我们将会在页面上看到显示的GUID
我们看到这里2个不同的Guids,因为通过浏览器调用HomeController类每次会创建2个Repository的对象,第一个对象通过Repository服务创建,第二个对象通过ProductSum服务创建(ProductSum在构造函数中有一个IRepository依赖)
这个实例没有共享,我们使用Scoped服务只有一个对象创建,这个对象和ProductSum服务共享, 接下来我们看一下Scoped服务例子
7.2 Scoped服务的例子
为了理解Scoped服务,在Program类中使用AddScoped方法注册Repository服务
builder.Services.AddScoped<IRepository, Repository>();
重新运行应用程序并且你将看到相同的GUIDS, 这意味着ServiceProvider创建了一个Repository对象类并且和ProductSum共享该实例,这是Scoped和Transient服务之间的不同
重新加载页面按下F5,你将看到新的GUID被生成(因为浏览器初始化了一个新的HTTP请求)
7.3 Singleton服务的例子
最后让我们看一下Singleton服务,因此通过使用AddSingleton方法修改Repository服务的注册方式
builder.Services.AddSingleton<IRepository,Repository>();
重新运行你的应用程序,你将看到这两个GUID值是相同的,刷新页面你注意GUID值并没有改变
第一次创建Repository对象它会在后面的每次请求共享该对象,当我们有多个客户端在浏览器中请求HomeController只会创建一个Repository对象并且共享该对象,所有的客户端每次请求都会看到相同的值