web-dev-qa-db-ja.com

Entity Framework Core:前の操作が完了する前に、このコンテキストで2番目の操作が開始されました

Entity Framework Coreを使用してASP.Net Core 2.0プロジェクトに取り組んでいます

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0"/>

そして、私のリストメソッドの1つでこのエラーが発生しています:

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()

これは私の方法です:

    [HttpGet("{currentPage}/{pageSize}/")]
    [HttpGet("{currentPage}/{pageSize}/{search}")]
    public ListResponseVM<ClientVM> GetClients([FromRoute] int currentPage, int pageSize, string search)
    {
        var resp = new ListResponseVM<ClientVM>();
        var items = _context.Clients
            .Include(i => i.Contacts)
            .Include(i => i.Addresses)
            .Include("ClientObjectives.Objective")
            .Include(i => i.Urls)
            .Include(i => i.Users)
            .Where(p => string.IsNullOrEmpty(search) || p.CompanyName.Contains(search))
            .OrderBy(p => p.CompanyName)
            .ToPagedList(pageSize, currentPage);

        resp.NumberOfPages = items.TotalPage;

        foreach (var item in items)
        {
            var client = _mapper.Map<ClientVM>(item);

            client.Addresses = new List<AddressVM>();
            foreach (var addr in item.Addresses)
            {
                var address = _mapper.Map<AddressVM>(addr);
                address.CountryCode = addr.CountryId;
                client.Addresses.Add(address);
            }

            client.Contacts = item.Contacts.Select(p => _mapper.Map<ContactVM>(p)).ToList();
            client.Urls = item.Urls.Select(p => _mapper.Map<ClientUrlVM>(p)).ToList();
            client.Objectives = item.Objectives.Select(p => _mapper.Map<ObjectiveVM>(p)).ToList();
            resp.Items.Add(client);
        }

        return resp;
    }

特にローカルで実行すると機能するため少し迷っていますが、ステージングサーバー(IIS 8.5)に展開するとこのエラーが発生し、正常に機能していました。モデルのいずれかの最大長を増やした後、エラーが表示され始めました。また、対応するビューモデルの最大長も更新しました。そして、非常に似ており、機能している他の多くのリストメソッドがあります。

Hangfireジョブを実行していましたが、このジョブは同じエンティティを使用しません。これが関連性があると考えることができるすべてです。これを引き起こしている可能性のあるアイデアはありますか?

32
André Luiz

例外は、_contextが同時に2つのスレッドによって使用されていることを意味します。同じリクエスト内の2つのスレッド、または2つのリクエストによる。

あなたの_contextは静的に宣言されているのでしょうか?すべきではありません。

または、コード内の別の場所から同じリクエストでGetClientsを複数回呼び出していますか?

すでにこれを行っているかもしれませんが、理想的には、DbContextdependency injection を使用します。つまり、スタートアップで AddDbContext() を使用することになります。 .cs、コントローラーコンストラクターは次のようになります。

private readonly MyDbContext _context; //not static

public MyController(MyDbContext context) {
    _context = context;
}

あなたのコードがこのようなものではない場合、私たちを見せてください。

26
Gabriel Luci

IoCとDependency Injectionを使用してDbContextが使用される可能性がある場所を解決するかどうかはわかりません。その場合、.NET Core(または他のIoCコンテナー)のネイティブIoCを使用していて、このエラーが発生する場合は、DbContextをTransientとして登録してください。行う

services.AddTransient<MyContext>();

OR

services.AddDbContext<MyContext>(ServiceLifetime.Transient);

の代わりに

services.AddDbContext<MyContext>();

AddDbContextは、コンテキストをスコープとして追加します。これは、複数のスレッドを操作するときに問題を引き起こす可能性があります。

また、非同期ラムダ式を使用する場合、 async/await operations はこの動作を引き起こす可能性があります。

それを一時的なものとして追加することには欠点もあります。各クラスはDbContextの独自のインスタンスを取得するため、コンテキストを使用している複数のクラスで一部のエンティティを変更することはできません。

36
alsami

このシナリオが原因で発生する場合があるこのエラー:asyncメソッドを呼び出しましたが、メソッドを呼び出す前にawaitを使用しませんでした。メソッドの前にawaitを追加することで問題が解決しました。ただし、回答は前述の質問に関連していない可能性がありますが、同様のエラーの解決に役立ちます。

10
Hamid Nasirloo

私は同じ問題に直面しましたが、その理由は上記のどれでもありませんでした。タスクを作成し、タスク内にスコープを作成し、コンテナーにサービスを取得するように依頼しました。それはうまくいきましたが、タスク内で2番目のサービスを使用し、新しいスコープにそれを要求するのを忘れました。そのため、2番目のサービスは既に破棄されたDbContextを使用していました。

Task task = Task.Run(() =>
    {
        using (var scope = serviceScopeFactory.CreateScope())
        {
            var otherOfferService = scope.ServiceProvider.GetService<IOfferService>();
            // everything was ok here. then I did: 
            productService.DoSomething(); // (from the main scope) and this failed because the db context associated to that service was already disposed.
            ...
        }
    }

私はこれをやるべきでした:

var otherProductService = scope.ServiceProvider.GetService<IProductService>();
otherProductService.DoSomething();

同じエラーが発生しました。 public async void ...ではなくpublic async Task ...として構築されたメソッドを呼び出したために発生しました。

1
Raynlaze

IQueryableをメソッドに渡して、同じコンテキストへの別のクエリの一部としてそのIQueryable 'リスト'を使用することで、このエラーを取得できました。

public void FirstMethod()
{
    // This is returning an IQueryable
    var stockItems = _dbContext.StockItems
        .Where(st => st.IsSomething);

    SecondMethod(stockItems);
}

public void SecondMethod(IEnumerable<Stock> stockItems)
{
    var grnTrans = _dbContext.InvoiceLines
        .Where(il => stockItems.Contains(il.StockItem))
        .ToList();
}

それを防ぐために、 approach here を使用し、SecondMethodへの呼び出しをSecondMethod(stockItems.ToList()に変更して、2番目のメソッドを渡す前にそのリストを具体化しました。

0
tomRedox

テーブルの各エントリに対してアクションを実行するバックグラウンドサービスがあります。問題は、DbContextの同じインスタンスですべてのデータを繰り返し変更すると、このエラーが発生することです。

このスレッドで述べたように、1つの解決策は、次のように定義することにより、DbContextの存続期間をtransientに変更することです。

services.AddDbContext<DbContext>(ServiceLifetime.Transient);

しかし、複数の異なるサービスで変更を行い、SaveChanges()メソッドを使用してそれらを一度にコミットするため、このソリューションは私の場合は機能しません。

私のコードはサービスで実行されるため、次のようなことをしていました

using (var scope = Services.CreateScope())
{
   var entities = scope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
   var writeService = scope.ServiceProvider.GetRequiredService<IWriteService>();
   foreach (Entity entity in entities)
   {
       writeService.DoSomething(entity);
   } 
}

単純なリクエストのようにサービスを使用できるようにします。この問題を解決するために、1つのスコープを2つに分割しました。1つはクエリ用で、もう1つは書き込み操作用です。

using (var readScope = Services.CreateScope())
using (var writeScope = Services.CreateScope())
{
   var entities = readScope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
   var writeService = writeScope.ServiceProvider.GetRequiredService<IWriteService>();
   foreach (Entity entity in entities)
   {
       writeService.DoSomething(entity);
   } 
}

そのように、効果的に使用されているDbContextの2つの異なるインスタンスがあります。

別の可能な解決策は、反復を開始する前に読み取り操作が終了したことを確認することです。私の場合、そもそもQueryableを使用することで回避しようとした操作のために、すべてをメモリにロードする必要がある多くの結果がある可能性があるため、これはあまり実用的ではありません。

0
NiPfi

私は同じメッセージを受け取りました。しかし、私の場合は意味がありません。私の問題は、誤って「NotMapped」プロパティを使用したことです。おそらく、Linq構文またはモデルクラスのエラーを意味する場合があります。エラーメッセージは誤解を招くようです。このメッセージの本来の意味は、同じdbcontextでasyncを同じリクエストで複数回呼び出すことはできないということです。

[NotMapped]
public int PostId { get; set; }
public virtual Post Post { get; set; }

このリンクの詳細を確認できます https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-this-context-before-a-previous-操作完了

0
Sharon Zhou