c# - DotNet 核心 Web API 依赖注入(inject)范围和处置

标签 c# dependency-injection .net-core

我有一个 dot net core 2.0 web api。该 API 使用 JWT 授权,通常返回 json 响应。

我将数据库上下文注入(inject)到一个存储库中,然后将存储库注入(inject)到 Controller 构造函数中,它适用于单个请求。

在 Startup.cs 中,我注册了存储库和上下文,如下所示:

services.AddMvc();
services.AddDbContext<SPContext>(cfg => cfg.UseSqlServer(_config["SPConnectionString"]));
services.AddScoped<ISPRepository, SPRepository>();

存储库构造函数:

private SPContext _context { get; }
public SPRepository(SPContext context, ILogger<SPRepository> logger)
    {
        _context = context;
        _logger = logger;
    }

Controller 构造函数:

private static ISPRepository _repository;
public ChartsController( ILogger<ChartsController> logger, ISPRepository repository)
{
        _repository = repository;
        _logger = logger;    
}

在我的方法中,我有:

 [HttpGet]
 [Route("api/v1/charts/dashboard/my")]
 public async Task<IActionResult> GetMyDashboards()
    {
        SPUser user = _repository.GetUserByOid(this.User.Identity.Name);
        return Ok(_repository.GetMyDashboards(user));
    }

对于此方法的单个 GET 请求,我得到了一个没有错误的结果,如果我在发送下一个请求之前等待执行完成,它也能正常工作。

在测试期间,我碰巧有多个请求同时发送到这条路由。结果是对第一个错误的响应,然后是对其余错误的“500”错误。

错误日志显示错误为:

System.InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

但是,如果我将存储库注入(inject)方法,而不是 Controller :

[HttpGet]
[Route("api/v1/charts/dashboard/my")]
public async Task<IActionResult> GetMyDashboards([FromServices]ISPRepository _repository)
    {
        SPUser user = _repository.GetUserByOid(this.User.Identity.Name);
        return Ok(_repository.GetMyDashboards(user));
    }

然后请求全部成功,即使同时触发也是如此。

我的理解是,注册为作用域的对象仅在单个请求的生命周期内可用,并且当新请求进入系统时将创建一个新实例。因此,根据我的理解,第二个请求应该导致创建一组新对象。

我注册服务或 Controller 的方式有问题吗?或者这是依赖注入(inject)的预期行为?

最佳答案

您应该删除 Controller 中 _repository 上的“static”属性。因为这意味着您的 Controller 的所有实例将共享同一个存储库。

当您在第一个请求仍在处理中时发出第二个请求时, 您将使用存储库的新实例创建 Controller 的新实例。这很好,但是因为 _repository 是静态的,它也将通过替换之前创建的 Controller 与 Controller 的第一个实例共享。

然后两个 Controller 将调用相同的底层 DbContext,从而导致您的问题。

当您手动注入(inject)存储库时,一切都是分离的,这就是它适合您的原因。

一个好的做法是让你的属性只读,以确保它们只在构造函数中初始化一次,以后不能被覆盖。

private readonly ISPRepository _repository;

关于c# - DotNet 核心 Web API 依赖注入(inject)范围和处置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51183167/

相关文章:

c# - Azure.Data.Tables.TableClient 是线程安全的吗?

java - 将 Guice 单例服务注入(inject) Quartz 任务、Stripes ActionBeans 和 ServletContextListeners 的正确方法?

javascript - Angular DI 注入(inject)的 super 代理的正确类型是什么?

c# - 默认情况下 IServiceProvider 是如何注入(inject)的?

c# - 从 TableAdapter 中删除行?

c# - 将数据发布到 asp.net Web API

C#如何将两个表达式组合成一个新的?

javascript - 处理 AngularJS 中的许多解析

.net - 使用 .Net Framework 的空间 anchor (Azure 混合现实服务)

azure - 无法在 Azure 机器人框架中调用 Luis