ASP.Net Core -- 依赖注入

一、什么是依赖注入(Denpendency Injection)

依赖:

当一个类需要另一个类协作来完成工作的时候就产生了依。比如我们在HomeController这个控制器需要对Student表来完成相关的功能。比如:查询,删除,添加,等等,我们由EF来操作数据库写业务逻辑来完成,所以我们写了一个interface类型名为IRepository.cs的服务。然后新建一个名为EfCoreRepository.cs来实现接口,该类主要写业务判断逻辑。这里HomeController就有一个IRepository的依

这里有一个设计原则:依于抽象,而不是具体的实现。所以我们给EfCoreRepository定义了一个接口,抽象了IRepository的行为。

 

那么什么是注入?如下:

每次提到依赖注入,想必就会提到“控制反转”,也就是IOC,在反转之前 ,我们先看看正转,如下:

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自始至终就一个服务,不管你横跨多少个请求

如图:

posted @ 2020-08-25 21:37  初晨~  阅读(432)  评论(1编辑  收藏  举报