zoukankan      html  css  js  c++  java
  • 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自始至终就一个服务,不管你横跨多少个请求

    如图:

  • 相关阅读:
    股票F10
    什么是盘口?
    CompletionPort
    WSAEvent
    SO_KEEPALIVE选项
    SO_LINGER
    shutdown和close的区别
    TIME_WAIT和CLOSE_WAIT状态区别
    什么是2MSL
    WSAStartup function
  • 原文地址:https://www.cnblogs.com/dcy521/p/13562194.html
Copyright © 2011-2022 走看看