【发布时间】:2016-12-21 03:03:47
【问题描述】:
我在 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 为空。 我通过
注入上下文访问器public UserService(IUserRepository userRepository, IHttpContextAccessor httpContextAccessor)
{
this.userRepository = userRepository;
this.httpContextAccessor = httpContextAccessor;
}
Answer: IHttpContextAccessor 需要注册为单例而非瞬态
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
【问题讨论】:
-
你能说明你是如何在容器上注册你的上下文的吗?
-
这感觉就像你在调用异步方法而不等待它们让它们并行运行?
-
@Klinger 我已经通过编辑更新了答案
-
@Pawel 我不确定你的意思,但我已经在编辑#2 中发布了代码路径
-
我看到了与此类似的问题 - 我最终不得不放弃 Async 以使用特定方法,因为它是唯一可行的方法。我所有的 DI 注册都是暂时的。我只能在测试服务器上重现这个问题(它很慢,并且有一个巨大的数据库表可供读取)。失败的 API 调用会同时进行 2 个查询并将它们组合在一起(按设计)。可能是问题的原因,但仍然使用异步没有明显的解决方案。
标签: c# entity-framework async-await entity-framework-core