ASP.NET Core依赖注入模式

依赖注入

​ 我们希望从Student对象数据源中查询特定的学生详细信息并将其显示在网页上。我们已经知道MVC中的Model包含了一组数据的类和管理该数据的逻辑信息。因此,为了表示想要显示的学生数据,可以使用以下Student类。

 public class Student
    {
        public int ID { get; set; }

        public string Name { get; set; }

        /// <summary>
        /// 主修科目
        /// </summary>
        public string Major { get; set; }

        public  string  Email{ get; set; }
    }

​ 我们在项目的根目录中创建一个Models文件夹。请注意,模型类并不需要强行放置在Models文件夹中,但将它们统一保存在Models文件夹中是一种很好的做法,便于以后找到它们。

​ 除了呈现数据的Student类,模型还包含一个管理模型数据的类。为了管理数据,即查询和保存学生数据,我们将使用以下IStudentRepository服务。目前,我们只有一个方法**GetStudent()**通过学生ID查询学生。

public interface IStudentRepository
    {
        Student GetStudent(int ID);
    }

​ 以下StudentRepository类提供了IStudentRepository接口的实现。目前,StudentRepository类中的Student类数据都是采用的硬编码。在以后,我们将为IStudentRepository接口提供另一种实现——从SQL Server数据库中查询和保存数据。

public class StudentRepository : IStudentRepository
    {
        private List<Student> _studentList;
        public StudentRepository()
        {
            _studentList = new List<Student>() {
                new Student(){ID=1,Name="张三",Major="计科",Email="zhangsan@qq.com"},
                new Student(){ID=2,Name="李四",Major="信安",Email="lisi@outlook.com"},
                new Student(){ID=3,Name="王五",Major="网工",Email="wangwu@foxmail.com"}
            };
        }

        public Student GetStudent(int ID)
        {
            return _studentList.FirstOrDefault(a => a.ID == ID);
        }
    }

​ 为了便于项目后期的管理和规范,我们需要在项目根目录中创建一个DataRepositories文件用于存放Student的接口IStudentRepository以及接口的实现服务StudentRepository。如图所示:

在这里插入图片描述

​ 现在我们将针对IStudentRepository接口进行编程,而不是具体实现StudentRepository。这种接口抽象化允许我们使用依赖注入,反过来也使应用程序灵活且易于单元测试,因此我们先来了解一下依赖注入。

了解ASP.NET Core中的依赖注入

​ 在维基百科中对依赖注入是这样解释的:“依赖注入是一种软件设计模式,指一个或多个依赖(或服务)被注入,通过引用传递,传入一个依赖对象(或客户端)并成为客户状态的一部分。该模式通过自身的行为分离了客户依赖的创建,这允许程序设计是松耦合的,同时遵循依赖倒置和单一职责原则。与服务定位器模式直接进行对比,它允许用户了解他们用来查找依赖的机制。

​ 由于依赖注入的概念难免有点晦涩,因此在这里,我们将通过一个简单的例子了解依赖注入。

​ 以前的做法是直接实例化类,代码如下。这种方式不推荐,因为它无法将系统解耦。

public string Index()
{
    //不推荐的做法
    var _studentRepository = new StudentRepository();
    return _studentRepository.GetStudent(1).Name;
}

​ 以下是推荐做法:

public class HomeController:Controller
{
    private readonly IStudentRepository _studentRepository;
    
    //使用构造函数注入的方式注入IStudentRepository
	public HomeController(IStudentRepository studentRepository)
    {
        _studentRepository = studentRepository;
    }
    
    //返回学生的名字
    public string Index()
    {
        return _studentRepository.GetStudent(1).Name;
    }
}


​ 代码说明如下:

  • Homecontroller通过IStudentRepository接口来查询Student数据。
  • 我们使用构造函数将IStudentRepository实例注入HomeController中,而不是HomeControllerIStudentRepository接口创建新的实例化。
  • 这称为构造函数依赖注入,因为我们使用构造函数来注入依赖项。
  • 请注意,我们将注入的_studentRepository依赖项分配了只读字段readonly。这是一个很好的做法,因为它可以防止在方法中误操作地为其分配另一个值,比如null。
  • 此时,如果我们运行项目,则会收到以下错误。

在这里插入图片描述

  • 这是因为如果有人请求实现IStudentRepository的对象,ASP.NET Core依赖注入容器不知道要提供哪个对象实例,原因如下:
    • IStudentRepository可能有多个实现。在我们的项目中只有一个实现,那就是StudentRepository
    • 顾名思义,StudentRepository使用内存中的学生模型数据。
    • 以后,我们将为IStudentRepository提供另一个实现,该实现是从SQL Server数据库中查询学生数据。
    • 现在,让我们先继续使用StudentRepository
  • 要修复InvalidOperationException错误,我们需要在ASP.NET Core中使用依赖注入组件,然后把StudentRepository类注册进去。
  • 我们在Startup类的ConfigureServices()方法中执行注册。

使用依赖注入注册服务

​ ASP.NET Core提供以下3种方法来使用依赖注入注册服务。我们使用的方法决定了注册服务的声明周期。

  • 📎AddSingleton()方法

    AddSingleton()方法创建一个Singleton服务。首次请求时会创建Singleton服务,然后所有后续所有请求都使用相同的实例。因此,通常每个应用程序只创建一次Singleton服务,并且在整个应用程序生命周期中使用该单个实例。

  • 📎AddTransient()方法

    AddTransient()方法可以称作暂时性模式,它会创建一个Transient服务。每次请求时,都会创建一个新的Transient服务实例。

  • 📎AddScoped()方法

    AddScoped()方法创建一个Scoped服务。在范围内的每个请求中创建一个新的Scoped服务实例。比如,在Web应用程序中,它为每个HTTP请求创建一个实例,但在同一HTTP请求的其他调用中使用相同的实例;在一个客户端请求中是相同的,而在多个客户端请求中是不同的。

​ 现在,要修复InvalidOperationException错误,让我们使用AddSingleton()向ASP.NET Core依赖注入容器注册StudentRepository类方法。

​ 在如下的代码中,如果调用IStudentRepository,则将调用StudentRepository的实例服务。

public void ConfigureServices(IServiceCollection services)
        {
    		//添加MVC服务
    		services.AddControllersWithViews(a => a.EnableEndpointRouting = false);
    		//添加依赖注入
            services.AddSingleton<IStudentRepository, StudentRepository>();
    		
        }

​ 也可以使用new关键字在HomeController中简单地创建StudentRepository类的实例,如下所示:

public class HomeController
    {
        private readonly IStudentRepository _studentRepository;

    	//使用构造函数注入的方式注入IStudentRepository
        public HomeController(IStudentRepository studentRepository)
        {
           //在构造函数中直接实例化服务而不是接口注入
            _studentRepository = new StudentRepository();
        }
        
    	//返回学生的名字
        public string Index()
        {
            return _studentRepository.GetStudent(1).Name;
        }
    }

​ 这使HomeControllerStudentRepository紧密耦合。以后我们会为IStudentRepository提供新的实现,如DatabaseStudentRepository。如果想要使用新的SqlStudentRepository,而不是DatabaseStudentRepository,我们必须更改HomeController中的代码。

​ 读者可能会想,这只是更改一行代码,因此并不难。

​ 但是如果我们在应用程序中的其它50个Controller中使用了这个StudentRepository,那么这50个Controller中的代码都得改,这不仅无聊而且容易出错。

​ 简而言之,在代码中使用new关键字创建依赖关系的实例会产生紧密耦合,使应用程序很难更改,最后导致项目无法重构和优化。而通过依赖注入的方式,而不会有这种问题的出现。通过这种方法,即使在应用程序的其它50个其它Controller中使用了StudentRepository,如果我们想用不同的实现替换它,则只需要在Startup.cs文件中更改一下一行代码。请注意,我们现在使用DatabaseStudentRepository而不是StudentRepository

public void ConfigureServices(IServiceCollection services)
        {
    		
    		services.AddControllersWithViews(a => a.EnableEndpointRouting = false);
            services.AddSingleton<IStudentRepository, DatabaseStudentRepository>();
    		
        }

​ 这样带来的效果使单元测试也变得更加容易,因为我们可以通过依赖注入轻松地交换依赖项。

🐱‍💻总结

​ MVC中的Model是包含了一组数据的类和管理该数据的逻辑信息。

在这里插入图片描述

对于依赖注入,总结如下:

  • ASP.NET Core中依赖注入提供的容器服务有以下3种:
    • AddSingleton()。
    • AddTransient()。
    • AddScoped()。
  • 依赖注入的优点如下:
    • 降低耦合度。
    • 让代码易于测试。
    • 提供了面向对象编程的机制。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值