非同期メソッドでEntity Frameworkコアが失敗する

async-await c# entity-framework entity-framework-core

質問

私はEFコアで非同期メソッドを使用しています - 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)
    ...

誰かが記事を賞賛したときに実行される。

この関数が一度に1つずつ実行されると、すべてが正常に実行されます(1つずつ)。

私はこの関数を複数回同時に実行すると、この例外が発生します。

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>();として登録する必要がありservices.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

受け入れられた回答

EntopフレームワークはScopedライフタイムを使用してサービスコンテナに追加する必要があります。リポジトリとサービスは、新しいインスタンスが作成され、必要に応じて挿入され、インスタンスが再利用されないことを保証するように一時的に構成する必要があります。

EFはすべての要求に対して作成され、要求が終了すると処理されるようにスコープを設定する必要があります。

これらのガイドラインに従って、注入されたインスタンスをコントローラのコンストラクタに格納する際に問題は発生しません。コントローラは、すべてのリクエストでインスタンス化され、スコープされたすべてのインジェクションされたインスタンスとともに、リクエストの最後に配置される必要があります。

このMicrosoftのドキュメントページでは、このすべてについて説明しています。


人気のある回答

他のコードでは、同じcontextを同時に使用しcontextこの質問と回答を確認してください。

Upvoteメソッドを含むクラスがシングルトンである場合、コンストラクタにcontextを格納しないことを確認してください。代わりに、各リクエスト(スコープ)ごとにIServiceProviderHttpContext.RequestServices )から取得するか、パラメータとして渡してください。



Related

ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ