ASP.Net Core -- 依赖注入
一、什么是依赖注入(Denpendency Injection)
依赖:
当一个类需要另一个类协作来完成工作的时候就产生了依
这里有一个设计原则:依
public readonly IRepository<Student> _repository;
public HomeController()
{
_repository = new InMemoryRepository();
}
在以上代码中,_repository自己创建了InMemoryRepository,然后就可以调用IRepository服务里边的方法,也就是调用InMemoryRepository,以为它实现了IRepository该接口,但是这样不好,你不应该自己创建它,而是应该由你的调用者给你。于是通过构造函数让外界把这两个依
public readonly IRepository<Student> _repository;
public HomeController(IRepository<Student> repository)
{
_repository = repository;
}
把依赖的创建丢给其它人,自己只负责使用,其它人丢给你依赖的这个过程理解为注入。其实在传统的开发当中,有的项目接口服务也不写,直接实例化实体类,然后直接使用,后期维护起来非常麻烦。
为了在业务变化的时候尽少改动代码可能造成的问题,所以要实现反转,让双方实现解耦,如果后期业务变动,比如我们要把实现服务的类InMemoryRepository给换掉,如果是在ASP.net Core中的话,这个时候只需要stratup里绑定服务的地方修改一下就行了:
services.AddScoped<IRepository<Student>, InMemoryRepository>();//把InMemoryRepository给换掉就行了
而控制器中啥也不用改,因为控制器那边调用是接口,和实现类没有关系。
那什么是容器?如下:
在ASP.Net Core中,如果服务太多,实现类也很多,这样就需要给每个接口绑定一个类,我们需要一个地方统一管理系统中所有的依
这个我暂时没用到。
二、在ASP.Net Core 中实现依赖注入
当我们写好一个服务(接口)时候,然后新建一个我们需要去写业务逻辑代码的类去实现它,然后在相应的地方,我们就可以调用相对应的服务,但是在ASP.Net Core中这样会报错,说找不到相应的服务,因为我们虽然写了服务,也实现了服务,但是框架并不知道,计算机并不知道,这个时候我们需要去将相关的服务和实现类进行绑定,告诉计算机,这个类要实现这个服务
在ASP.Net Core中绑定服务有三种方式,需要在startup.cs文件中的ConfigureServices方法中进行绑定,如下:
第一种:AddSingleton
services.AddSingleton<IRepository<Student>, InMemoryRepository>();
第二种:AddScoped
services.AddScoped<IRepository<Student>, InMemoryRepository>();
第三种:AddTransient
services.AddTransient<IRepository<Student>, InMemoryRepository>();
1:先来说说AddSingleton():
Addsingleton():顾名思义, Addsingleton()方法创建一个Singleton服务。首次请求时会创建会Singleton服务,然后,所有后续的请求中都会使用相同的实例。因此,通常,每个应用程序只创建一次Singleton服务,并且在整个应用程序生命周期中使用该单个实例,听着有点蒙,看代码,如下:
新建一个Student类:
public class Student
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
然后新建一个IRepository服务:
public interface IRepository<T> where T : class { IEnumerable<T> GetAll();
T GetDetailById(int id);
T Add(T model); }
新建实现类InMemoryRepository.cs:
public class InMemoryRepository : IRepository<Student>
{
public readonly List<Student> _students;
public InMemoryRepository()
{
_students= new List<Student>
{
new Student
{
Id = 1,
FirstName = "张三",
LastName = "嗯嗯",
BirthDate=new DateTime(1999,5,6)
},
new Student
{
Id = 2,
FirstName = "李四",
LastName = "哈哈",
BirthDate=new DateTime(1979,5,6)
},
new Student
{
Id = 3,
FirstName = "王五",
LastName = "嘿嘿",
BirthDate=new DateTime(1899,5,6)
}
};
}
public Student Add(Student model)
{
var maxId = _students.Max(x => x.Id);
model.Id = maxId + 1;
_students.Add(model);
return model;
}
public IEnumerable<Student> GetAll()
{
return _students;
}
}
绑定服务:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IRepository<Student>, InMemoryRepository>();
}
这个时候就可以使用IRepository这个服务,调用里边的方法了,如下:
我们在Student控制器中和Create视图都实现注入,如下:
StudentController.cs:
public class StudentController : Controller
{
private readonly IRepository<Student> _repository;
public StudentController(IRepository<Student> repository)
{
_repository = repository;
}
public IActionResult Index()
{
var list = _repository.GetAll();
return View(list);
}
public IActionResult Create()
{
return View();
}
[HttpPost]
public IActionResult Create(Student student)
{
_repository.Add(student);
return View();
}
}
Create.cshtml:
@model Student
@inject IRepository<Student> _irepository
<form method="post" enctype="multipart/form-data" asp-action="Create">
<div class="form-group">
<input asp-for="FirstName" class="form-control" />
</div>
<br />
<input type="submit" value="save" class="btn btn-primary" />
</form>
<h1>学生总人数:@_irepository.GetAll().Count().ToString()</h1>
<a asp-action="Index" asp-controller="Student">返回index页</a>
实现的功能很简单,在Create页面添加学生信息,点击提交按钮,跳转到Student控制器下的Create方法里,然后调用IRepository服务里的Add方法,最后直接返回到Create页面
点击保存,页面刷新,学生总人数加一,不停的点击保存,学生总人数不停的加,就算刷新页面,返回列表表,新添加的学生信息依然存在。
这是为什么呢?因为使用的Singleton服务,整个应用程序生命周期以内只创建一个实例,不论你怎么添加, 用的始终都是一个服务,不论你在哪里调用,用的始终都是第一次被创建的服务
2:AddScoped()
此方法创建一个scoped服务,在同一个Scoped内只初始化一个实例 ,可以理解为( 每一个Request级别只创建一个实例,同一个HTTP Request会在一个 scoped内),有点晕,看代码:
还是以上边代码为例,将服务绑定改为:
services.AddScoped<IRepository<Student>, InMemoryRepository>();
再点击保存,这个时候发现和之前不一样了,如:
1:点击保存,学生总人数为4,但是继续点击保存还是4,永远为4
2:点击返回列表页,学生数据还是3条数据
3:再返回原先的Create视图页,学生总人数又变成3条
为什么呢?
因为每一次HTTP请求,都会创建的新的示例,比如:
• 点击保存,发出HTTP post请求,创建新的实例,添加一条学生数据,学生总人数变为4,
• 再点击保存,又发生HTTP post请求,又创建一个实例,因为采用的是硬编码的形式,当第二次请求发出的时候,第一次保存的数据已经销毁,然后在原先3条数据的基础上,添加一条数据
• 当点击返回列表页时候,发生HTTP get请求,创建新的实例,执行查询数据操作,所以是3条数据,因为这3条数据写死的
• 再点击添加一条数据,返回Create视图,学生总人数又变为3条,因为返回Create视图时,发出的HTTP get请求
• 点击保存,虽然还是返回到Create视图,但是发出的HTTP post请求,尽管在一个视图,所以学生总人数变为4。
4:所以,这个是我们在日常开发中经常使用的,因为连接数据库后,每次都是HTTP请求,我们都需要创建新的实例
3:AddTransient()
此方法创建一个Transient服务,每次请求都会创建新的实例,这个就有点简单了,如下:
services.AddTransient<IRepository<Student>, InMemoryRepository>();
当我们再点击保存按钮,永远都是3条数据,因为点击一次创建一个新的实例,点击保存,发出HTTP post请求,完了之后,返回时候时候又会创建新的实例。
总结一下:
1:在使用Scoped服务时候,点击保存发出HTTP post请求,添加数据,返回视图,呈现数据,都在一个范围内,一连串的动作。
2:而Transient可以理解为暂时性的服务
3:Singleton自始至终就一个服务,不管你横跨多少个请求
如图: