wake-up-neo.com

Entity Framework Core: In diesem Kontext wurde eine zweite Operation gestartet, bevor eine vorherige Operation abgeschlossen wurde

Ich arbeite an einem ASP.Net Core 2.0-Projekt mit Entity Framework Core 

<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"/>

Und in einer meiner Listenmethoden bekomme ich diesen Fehler:

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()

Das ist meine Methode:

    [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;
    }

Ich bin etwas verloren, vor allem, weil es funktioniert, wenn ich es lokal ausführe. Wenn ich es jedoch auf meinem Staging-Server (IIS 8.5) bereitstelle, wird mir dieser Fehler angezeigt und er hat normal funktioniert. Der Fehler wurde angezeigt, nachdem ich die maximale Länge eines meiner Modelle erhöht habe. Ich habe auch die maximale Länge des entsprechenden Ansichtsmodells aktualisiert. Und es gibt viele andere Listenmethoden, die sehr ähnlich sind und funktionieren.

Ich hatte einen Hangfire-Job, aber dieser Job verwendet nicht dieselbe Entität. Das ist alles, was ich für relevant halten kann. Irgendwelche Ideen, woran das liegen könnte?

14
André Luiz

Die Ausnahme bedeutet, dass _context von zwei Threads gleichzeitig verwendet wird. Entweder zwei Threads in derselben Anfrage oder zwei Anfragen.

Ist Ihr _context möglicherweise als statisch deklariert? Es sollte nicht sein.

Oder rufen Sie GetClients in derselben Anforderung mehrmals an einem anderen Ort in Ihrem Code auf?

Möglicherweise machen Sie dies bereits, aber im Idealfall würden Sie Dependency Injection für Ihre DbContext verwenden, was bedeutet, dass Sie AddDbContext() in Ihrer Startup.cs verwenden und Ihr Controller-Konstruktor etwas aussieht so was:

private readonly MyDbContext _context; //not static

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

Wenn Ihr Code nicht so ist, zeigen Sie uns und vielleicht können wir weiter helfen.

15
Gabriel Luci

Ich bin mir nicht sicher, ob Sie IbC und Dependency Injection verwenden, um Ihren DbContext an beliebigen Stellen aufzulösen. Wenn Sie nativen IoC von .NET Core (oder einem anderen IoC-Container) verwenden und diesen Fehler erhalten, müssen Sie Ihren DbContext als Transient registrieren. Tun

services.AddTransient<MyContext>();

OR

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

anstatt

services.AddDbContext<MyContext>();

AddDbContext fügt den Kontext als Bereich hinzu, was bei der Arbeit mit mehreren Threads Probleme verursachen kann. 

Auch async/await -Operationen können dieses Verhalten verursachen, wenn Sie asynchrone Lambda-Ausdrücke verwenden.

Das Hinzufügen als vorübergehend hat auch seine Schattenseiten. Sie können keine Änderungen an einer Entität über mehrere Klassen vornehmen, die den Kontext verwenden, da jede Klasse eine eigene Instanz von DbContext erhält.

24
alsami

Dieser Fehler wurde in einigen Fällen durch dieses Szenario verursacht: Sie rufen eine async -Methode auf, haben jedoch vor dem Aufruf der Methode await nicht verwendet. Mein Problem wurde durch Hinzufügen von Warten vor der Methode gelöst. Die Antwort kann sich jedoch nicht auf die erwähnte Frage beziehen, kann jedoch zu ähnlichen Fehlern führen.

4
Hamid Nasirloo

Ich war mit demselben Problem konfrontiert, aber der Grund war keiner der oben aufgeführten. Ich habe eine Aufgabe erstellt, einen Bereich innerhalb der Aufgabe erstellt und den Container nach einem Dienst gefragt. Das hat gut funktioniert, aber dann habe ich einen zweiten Dienst innerhalb der Aufgabe verwendet und ich habe auch vergessen, ihn in den neuen Bereich zu bringen. Aus diesem Grund verwendete der zweite Dienst einen DbContext, der bereits entsorgt wurde.

                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.
                        ...

                    }
                }

Ich hätte das tun sollen:

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

Ich hatte den gleichen fehler Es ist passiert, weil ich eine Methode aufgerufen habe, die als public async void ... anstatt public async Task ....

1
Raynlaze

Ich habe es geschafft, diesen Fehler zu erhalten, indem ich ein IQueryable an eine Methode übergeben habe, die dann diese IQueryable-Liste als Teil einer anderen Abfrage im selben Kontext verwendet hat.

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();
}

Um dies zu verhindern, benutzte ich den Ansatz hier und materialisierte diese Liste, bevor ich die zweite Methode übergab, indem ich den Aufruf von SecondMethod auf SecondMethod(stockItems.ToList() änderte.

0
tomRedox

Ich habe einen Hintergrunddienst, der für jeden Eintrag in einer Tabelle eine Aktion ausführt. Das Problem besteht darin, dass dieser Fehler auftritt, wenn ich einige Daten in derselben Instanz von DbContext durchlaufe und ändere.

Eine Lösung, wie in diesem Thread erwähnt, besteht darin, die Lebensdauer von DbContext in transient zu ändern, indem Sie sie wie folgt definieren

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

da ich jedoch Änderungen an mehreren verschiedenen Diensten vornehme und diese gleichzeitig mit der SaveChanges() -Methode festschreibe, funktioniert diese Lösung in meinem Fall nicht.

Da mein Code in einem Dienst ausgeführt wird, habe ich so etwas getan

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);
   } 
}

in der Lage zu sein, den Dienst zu nutzen, als wäre es eine einfache Anfrage. Um das Problem zu lösen, teile ich den einzelnen Bereich in zwei Bereiche auf, einen für die Abfrage und den anderen für die Schreiboperationen:

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);
   } 
}

Auf diese Weise werden effektiv zwei verschiedene Instanzen des DbContext verwendet.

Eine andere mögliche Lösung wäre, sicherzustellen, dass der Lesevorgang beendet wurde, bevor die Iteration gestartet wird. Das ist in meinem Fall nicht sehr praktisch, da es eine Menge Ergebnisse geben könnte, die alle in den Speicher geladen werden müssten, um die Operation durchzuführen, die ich durch die Verwendung eines Queryable zunächst vermeiden wollte.

0
NiPfi

Ich habe dieselbe Nachricht bekommen. Aber in meinem Fall macht es keinen Sinn. Mein Problem ist, dass ich versehentlich eine "NotMapped" -Eigenschaft verwendet habe. .__ In einigen Fällen bedeutet dies wahrscheinlich nur einen Fehler der Linq-Syntax oder der Modellklasse. Die Fehlermeldung scheint irreführend zu sein. Die ursprüngliche Bedeutung dieser Nachricht ist, dass Sie nicht mehrmals in derselben Anforderung async für denselben dbcontext aufrufen können.

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

Sie können diesen Link für Details überprüfen. https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-thiscontext-vor- -operation abgeschlossen

0
Sharon Zhou