繁体   English   中英

.NET Core/EF 6 - 依赖注入范围

[英].NET Core/EF 6 - Dependency Injection Scope

我目前正在使用 EF 6 设置 .NET Core 应用程序,但在理解各种依赖项注册方法的适当用法时遇到了一些麻烦。 据我了解:

  • Transient :在需要时创建对象(即每次请求时创建一个新实例)
  • Singleton :在应用程序启动时创建的单个实例,可用于所有后续请求
  • Scoped :在请求期间可用

特别是在我的情况下,我设置了一对 DbContexts(基于 CQRS 模式)来处理我注册为Scoped 的数据库查询/命令:

services.AddScoped((_) => new TestCommandContext(Configuration["Data:TestConnection:ConnectionString"]));
services.AddScoped((_) => new TestQueryContext(Configuration["Data:TestConnection:ConnectionString"]));

这是根据 ASP.NET Getting Started with ASP.NET 5 and Entity Framework 6文档:

Context 应该在每个范围内解析一次,以确保性能并确保 Entity Framework 的可靠运行

然后我注册各自的 UOW 类:

services.AddTransient<ITestCommandUnit, TestCommandUnit>();
services.AddTransient<ITestQueryUnit, TestQueryUnit>();

我根据这篇文章在这里使用Transient ,这表明:

使用 Transient 范围注册的服务会在应用程序中需要时随时创建。 这意味着每次执行(创建依赖的方法)时,依赖注入框架都会创建(注册服务)类的一个新实例。

基于这种理解,我也在Scoped下注册我的存储库和服务类:

services.AddScoped<ITestCommandRepository, TestCommandRepository>();
services.AddScoped<ITestQueryRepository, TestQueryRepository>();

services.AddScoped<ITestCommandService, TestCommandService>();
services.AddScoped<ITestQueryService, TestQueryService>();

然后根据需要在我的控制器中调用我各自的服务层方法:

public class TestController : BaseController
{
    private ITestQueryService testQueryService;

    // Get new object of type TestQueryService via DI
    public TestController(ITestQueryService testQueryService)
    {
        this.testQueryService = testQueryService;
    }

    [HttpGet]
    public IActionResult Edit(int id)
    {
        if (id > 0)
        {
            EditViewModel viewModel = new EditViewModel();
            viewModel.TestObject = testQueryService.GetById(id);
            return View(viewModel);
        }
        else
        {
            return RedirectToAction("Error", new { errorMessage = "No object with the specified Id could be found." });
        }
    }
}

在测试中,此配置似乎有效,并且将 DbContext(s) 设置为Scoped是有意义的 - 每次请求时都创建新的上下文对象似乎没有必要/效率低下。

但是,其他对象的Transient / Singleton / Scoped之间的选择是我迷失的地方。 有人可以帮助我了解这种特定模式实现的最佳配置吗?

上述设置有效,但我正在寻找更多关于为什么我应该使用我所做的范围的理解。 (即瞬态是我的 UOW 课程的最佳选择吗?为什么在这种情况下它比单例更好?等等。)

一般来说,我的经验法则是:

  1. Scoped - 是要走的路,保存缓存和你的头发,因为状态是为整个请求共享的。 没有并发问题(所有范围服务共享单线程)。 如果在单个请求中多次使用类,则不会创建实例。 如果我不知道应该如何注册课程,我会选择范围。 通常,您还需要在单个请求中多次使用某些内容 - 您可以计算一次,并在字段中设置值,因此对客户的CreditLimit的下一个查询将不会访问数据存储。

  2. 单例适用于缓存(服务器范围)、配置类、考虑到多线程设计的对象(多个请求)。 请注意,单例不应该依赖于作用域对象。 还要注意在多个线程中调用单例。 如果您需要单例来处理请求数据,请将其作为函数参数传递。

  3. 在我的应用程序中,临时注册非常罕见。 我将它用于具有内部状态的类,并且它们可以多次使用,并且不应共享该状态。 通常是实用程序或框架类。

示例范围类? SqlConnection - 您不想从单个请求打开多个连接到数据库(因为它是由连接池处理的)。 还使用该连接提供服务(服务只做一件事,因此不需要多个实例)。 Asp 控制器。

示例单例? 今天浏览最多的文章。 邮政编码验证器(没有依赖关系,但可以是单例)。

示例瞬态? 想想如果该请求中的所有您的列表都共享状态会发生什么。 列表不是服务请求,而是您的代码,并且可以在单个请求期间用于不同目的。

请记住,如果单例具有瞬态或作用域依赖性,则在单例被释放(应用程序回收)之前,它不会被释放。 因此,作用域的事物可以依赖于单例,但单例不能依赖于作用域。

说到 CQRS 和 DbContext - 在我的应用程序中,我有一个 DbContext,由命令和查询共享。 一切都在每个生命周期范围内注册(命令或查询在完成后不会保留状态,因此它们可以被重用。将其设置为瞬态也可以)。 另一个例子是为 html 元素生成唯一 id 的类。 它被注册为作用域,并在每次查询新 id 时增加内部计数器。 如果 class 是瞬态的,那么在从下一个 class 调用时它会丢失其状态。

请注意,有些人有其他观点。 如果您使用多个生命周期作用域,最好转移到瞬态依赖项。 如果我需要多次使用单个依赖项,我喜欢通过工厂,并且我尝试在我的应用程序中只有一个生命周期范围。

  • 瞬态对象总是不同的; 为每个控制器和每个服务提供一个新实例。
  • 作用域对象在一个请求中是相同的,但在不同的请求中是不同的
  • 单例对象对于每个对象和每个请求都是相同的(无论 ConfigureServices 中是否提供了实例)

在您的情况下,您重新注入的服务不依赖于同一请求中其他对象的状态。 您使用该服务来获取 Db 中的对象。 临时或范围服务都可以工作。

如果在同一个请求中您需要根据同一个请求中的计算更改对象的状态,您将需要使用从开始到结束都存在于同一个请求中的对象(即:作用域)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM