首页 文章

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

提问于
浏览
8

我目前正在使用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文档:

每个范围应解决一次上下文,以确保性能并确保实体框架的可靠运行

我正在注册相应的UOW类:

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

我根据this文章在这里使用 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设置为 Scoped 是有意义的 - 每次请求时创建新的上下文对象似乎是不必要/低效的 .

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

前面提到的设置正在运行,但我正在寻找更多的理解为什么我应该使用我做的范围 . (即 Transient 是我的UOW课程的最佳选择?为什么在这种情况下它比 Singleton 更好?等等)

2 回答

  • -1

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

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

    • Singleton适用于缓存(服务器范围),配置类,设计时考虑了多个线程的对象(多个请求) . 请注意,单例不应该依赖非单例对象 . 还要注意多线程中的单身人士 .

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

    示例范围类? SqlConnection - 您不希望从单个请求(通常)打开与db的多个连接 . 也使用该连接进行服务(服务做一件事,所以不需要多个实例) . Asp控制器 .

    单例示例?今天观看最多的文章 . Zip-code验证器(无依赖关系,纯方法) .

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

    请记住,如果单例具有瞬态或作用域依赖性,则在处理单例(应用程序循环)之前不会处置它 . 因此,作用域的东西可能依赖于单一的东西,但是单身者不能依赖于作用域 .

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

  • 2
    • 瞬态物体总是不同的;为每个控制器和每个服务提供一个新实例 .

    • Scoped对象在请求中是相同的,但在不同请求中是不同的

    • Singleton对象对于每个对象和每个请求都是相同的(无论ConfigureServices中是否提供了实例)


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

    如果在同一请求中您需要根据同一请求中的计算更改对象的状态,则需要使用从开头到结尾的同一请求中的对象(即:作用域) .

相关问题