.NET Core中AddSingleton() 、AddScoped() 和AddTransient()方法的区别

339 阅读3分钟

AddSingleton() 、AddScoped() 和AddTransient()方法的区别

服务生存期

可以使用以下任一生存期注册服务

  • 单例
  • 作用域
  • 暂时

服务的注册和调用

向依赖注入容器中注册服务,容器为接口IStudentRepository,服务为接口的实现类MockStudentRepository

public void ConfigureServices(IServiceCollection services)
   {
       services.AddSingleton<IStudentRepository, MockStudentRepository>();//用于向 依赖注入容器 中注册一个单例生存期服务
   	 //services.AddScoped<IStudentRepository, MockStudentRepository>();
     //services.AddTransient<IStudentRepository, MockStudentRepository>();
   }

HomeController类的构造函数

    public HomeController(IStudentRepository studentRepository)
    {
        _studentRepository = studentRepository;
    }

HomeController类中的Create()方法,用于添加学生信息的表单的处理逻辑。

[HttpPost]
        public IActionResult Create(Student student)
        {
            if (ModelState.IsValid)
            {

                Student newStudent = _studentRepository.Add(student);//此处学生人数加1
                //return RedirectToAction("Details", new { id = newStudent.Id });
            }
            return View();//返回页面时需要注入一次服务
        }

我们使用@inject 指令将 IStudentRepository 服务注入到 Create 视图中。 我们使用注入的服务来显示存储库中的学生总数(初始为5人)。

@model Student @inject IStudentRepository _studentRepository//添加依赖注入容器
@{ ViewBag.Title ="创建学生信息"; }

<form asp-controller="home" asp-action="create" method="post" class="mt-3">
  <div asp-validation-summary="All" class="text-danger"></div>

  <div class="form-group row">
    <label asp-for="Name" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <input asp-for="Name" class="form-control" placeholder="请输入名字"/>
      <span asp-validation-for="Name" class="text-danger"></span>
    </div>
  </div>

  <div class="form-group row">
    <label asp-for="Email" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <input
        asp-for="Email"
        class="form-control"
        placeholder="请输入邮箱地址"
     />
      <span asp-validation-for="Email" class="text-danger"></span>
    </div>
  </div>

  <div class="form-group row">
    <label asp-for="Major" class="col-sm-2 col-form-label"></label>
    <div class="col-sm-10">
      <select
        asp-for="Major"
        class="custom-select mr-sm-2"
        asp-items="Html.GetEnumSelectList<MajorEnum>()"
      >
        <option value=""> 请选择</option>
      </select>
      <span asp-validation-for="Major" class="text-danger"></span>
    </div>
  </div>

  <div class="form-group row">
    <div class="col-sm-10">
      <button type="submit" class="btn btn-primary">创建</button>
    </div>
  </div>

  <div class="form-group row">
    <div class="col-sm-10">
      学生总人数 = @_studentRepository.GetAllStudents().Count().ToString()//显示学生总人数
    </div>
  </div>
</form>

AddSingleton()方法

用于注册单例生存期服务

使用 AddSingleton 方法注册服务时,它并不会立即创建该服务的实例,它将在第一次请求该服务时创建实例,并将该实例缓存起来以供后续请求使用。因此,单例生存期服务在第一次调用时创建,并在整个应用程序的生命周期中保持相同的实例。

当我们点击创建按钮时,浏览器向服务器发起 HttpPost 请求,服务器收到请求,将请求映射到Home控制器、Create操作方法,注意,在初始化HomeController时,构造器需要IStudentRepository类型参数,MockStudentRepository的实例此时被注入。此时根据MockStudentRepository类中的硬编码,学生总数为5。

自此,此实例一直存在,其他Http请求或服务调用仍使用此实例,直到程序关闭。所以我们点击创建后,学生总数会正常增加。

AddScoped() 方法

用于注册作用域生存期服务

使用AddScoped()方法注册服务,在同一Http请求下,程序共享一个MockStudentRepository实例,该实例在服务首次调用时创建,在Http请求结束后被释放。新的请求会调用新的实例。

在Create方法中,return View()页面中调用MockStudentRepository服务(调用该服务并没有发起新的请求)。

当每次点击创建按钮时,会产生一个新的HttpPost请求,MockStudentRepository实例会在初始化HomeController类时被注入,在return View()IStudentRepository _studentRepository容器中仍然注入该实例。所以第一次点击创建按钮添加学生时,发起了一次请求并在当前页面时调用服务时调用同一实例,学生总数会正常加1为6。但当我们再次点击创建按钮时,会发起新的Http请求,新的实例被创建,会重复之前由5到6的流程,而不是继续增加。

AddTransient()方法

用于注册暂时生存期服务

暂时生存期服务是每次向服务容器注入服务时创建的。 这种生存期适合轻量级、 无状态的服务。

使用AddTransient()方法注册服务,会在每次向依赖注入容器中注入服务时都注入新的MockStudentRepository实例,因此在return View()后结果不会变,始终为学生列表初始的总数5。