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

时间:2016-05-11 19:57:45

标签: c# entity-framework dependency-injection asp.net-core unit-of-work

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

  • 瞬态:在需要时创建对象(即每次请求的新实例)
  • 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范围注册的服务。这意味着每次执行(创建依赖项的方法)时,依赖注入框架都会创建(注册服务)类的新实例。

基于这种理解,我使用 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 是我的UOW课程的最佳选择?为什么在这种情况下它比 Singleton 更好?等等。)

2 个答案:

答案 0 :(得分:2)

通常我的经验法则是:

  1. 作用域-是一种方法,可以节省缓存和头发,因为状态是为整个请求共享的。没有并发问题(所有作用域服务共享一个线程)。如果在单个请求中多次使用class,则保存实例。如果我不知道应该如何注册课程,我会进行范围界定。同样通常,您在单个请求中需要多次处理-您可以计算一次,然后在字段中设置值,因此对CreditLimit个客户的下一次查询将不会访问数据库。

  2. Singleton非常适合用于缓存(服务器范围),配置类,考虑了多个线程的对象(多个请求)。请注意,单例不应该依赖于非单例对象。还要注意在多个线程中调用单例。

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

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

单例示例?今天观看次数最多的文章。邮政编码验证器(无依赖项,纯方法)。

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

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

说到CQRS和DbContext-在我的应用程序中,我有单个DbContext,由Commands和Queries共享。所有内容都按生命周期范围进行了注册(命令或查询完成后未保留任何状态,因此可以重复使用。将其设置为瞬态也可以)。 另一个示例是为html元素生成唯一ID的类。它被注册为作用域,并且每次查询新的id时都会递增内部计数器。如果类是瞬态的,则从下一个类中调用时它将丢失其状态。

答案 1 :(得分:-1)

  • 瞬态物体总是不同的;提供了一个新实例 每个控制器和每项服务。
  • Scoped对象在请求中是相同的,但不同 不同的要求
  • Singleton对象对于每个对象和每个请求都是相同的 (无论是否在ConfigureServices中提供实例)

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

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