我有一个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));
}
然后,即使同时触发,所有请求也都成功。
我的理解是注册为作用域的对象仅在单个请求的生命期内可用,并且当新请求进入系统时将创建一个新实例。因此,据我所知,第二个请求应该导致创建了一组新的对象。
我注册服务或控制器的方式是否有问题?还是这是依赖注入的预期行为?
答案 0 :(得分:0)
您应该在控制器中的_repository上删除“ static”属性。因为这意味着您控制器的所有实例将共享一个相同的存储库。
在您仍在处理第二个请求时触发第二个请求时, 您将使用存储库的新实例创建控制器的新实例。没关系,但是因为_repository是静态的,所以通过替换先前创建的实例,它也将与控制器的第一个实例共享。
然后,两个控制器将调用相同的基础DbContext,从而引起您的问题。
当您手动注入存储库时,所有内容都会分离,这就是为什么它对您有效的原因。
一种好的做法是使您的属性为只读,以确保它们仅在构造函数中初始化一次,以后不能覆盖。
private readonly ISPRepository _repository;