我正在使用EF Core的异步方法 - 通过内置的DI获取上下文
public async Task<bool> Upvote(int userId, int articleId)
{
var article = await context.Articles
.FirstOrDefaultAsync(x => x.Id == articleId);
if (article == null)
{
return false;
}
var existing = await context.Votes
.FirstOrDefaultAsync(x => x.UserId == userId
&& x.ArticleId == articleId);
if (existing != null)
...
当有人投票给一篇文章时就会运行。
如果此函数一次一个地运行(一个接一个),一切运行正常。
当我同时多次点击此功能时,我遇到了这个例外:
fail: Microsoft.EntityFrameworkCore.Query.Internal.MySqlQueryCompilationContextFactory[1]
An exception occurred in the database while iterating the results of a query.
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.<BufferAllAsync>d__12.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
断点点击:
var existing = await context.Votes.FirstOrDefaultAsync(x => x.UserId == userId && x.ArticleId == articleId);
我也收到此错误:Message [string]:"A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe."
有哪些可能的解决方案?
编辑1: 这就是我设置上下文的方式: 在Startup.cs中,我配置了上下文:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ArticlesContext>(options =>
options.UseMySql(Configuration.GetConnectionString("ArticlesDB")));
...
然后我将它注入包含类的构造函数中:
private ArticlesContext context;
private ILoggingApi loggingApi;
public VoteRepository(ArticlesContext context, ILoggingApi loggingApi)
{
this.context = context;
this.loggingApi = loggingApi;
}
编辑2: 我正在等待一直到控制器:
public async Task<bool> Upvote(int articleId)
{
return await this.votesRepository.Upvote(userId, articleId);
}
然后在控制器......
[HttpPost]
[Route("upvote")]
public async Task<IActionResult> Upvote([FromBody]int articleId)
{
var success = await votesService.Upvote(articleId);
return new ObjectResult(success);
}
编辑3:
我已将我的服务/回购更改为瞬态而非单身,但现在我遇到了另一个问题:
public int getCurrentUserId()
{
if (!httpContextAccessor.HttpContext.User.HasClaim(c => c.Type == "UserId"))
{
return -1;
}
这是异步问题 - 但这一次,HttpContext为null。 我正在通过
注入上下文访问器public UserService(IUserRepository userRepository, IHttpContextAccessor httpContextAccessor)
{
this.userRepository = userRepository;
this.httpContextAccessor = httpContextAccessor;
}
答案:IHttpContextAccessor需要注册为单例而非瞬态
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
答案 0 :(得分:3)
应使用Scoped生命周期将实体框架添加到服务容器中,应将repo和services配置为transient,以便根据需要创建和注入新实例,并保证不重用实例。
EF应该作为范围,以便在每个请求上创建它,并在请求结束后处理。
遵循这些指导原则,我没有看到将注入的实例存储在控制器构造函数上的问题。控制器应在每个请求上实例化,并在请求结束时与所有作用域注入的实例一起处理。
答案 1 :(得分:0)
其他一些代码同时使用相同的context
。检查this question and answer。
如果包含Upvote
方法的类是单例 - 检查您是否未在构造函数中存储context
,而是应该从IServiceProvider
(HttpContext.RequestServices
)获取每个请求(范围),或将其作为参数传递。