DotNetCore WebAPI依赖注入范围和处置

时间:2018-07-05 03:54:01

标签: c# dependency-injection .net-core

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

我将数据库上下文注入到存储库中,然后将存储库注入到控制器构造函数中,并且它对于单个请求都可以正常工作。

在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.

但是,如果我将存储库注入方法而不是控制器中:

[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));
    }

然后,即使同时触发,所有请求也都成功。

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

我注册服务或控制器的方式是否有问题?还是这是依赖注入的预期行为?

1 个答案:

答案 0 :(得分:0)

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

在您仍在处理第二个请求时触发第二个请求时, 您将使用存储库的新实例创建控制器的新实例。没关系,但是因为_repository是静态的,所以通过替换先前创建的实例,它也将与控制器的第一个实例共享。

然后,两个控制器将调用相同的基础DbContext,从而引起您的问题。

当您手动注入存储库时,所有内容都会分离,这就是为什么它对您有效的原因。

一种好的做法是使您的属性为只读,以确保它们仅在构造函数中初始化一次,以后不能覆盖。

private readonly ISPRepository _repository;