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


89

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 erhalte 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 ein bisschen verloren, vor allem, weil es funktioniert, wenn ich es lokal ausführe, aber wenn ich es auf meinem Staging-Server (IIS 8.5) bereitstelle, erhalte ich diesen Fehler und es funktionierte normal. Der Fehler trat auf, nachdem ich die maximale Länge eines meiner Modelle erhöht hatte. 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 ausgeführt, aber dieser Job verwendet nicht dieselbe Entität. Das ist alles, was ich für relevant halten kann. Irgendwelche Ideen, was dies verursachen könnte?


1
Überprüfen Sie dies .
Berkay

2
@Berkay Ich habe das und viele andere ähnliche Fragen gesehen und ausprobiert. Meine Methode war asynchron und ich habe sie synchronisiert, um diese Probleme zu vermeiden. Ich habe auch versucht, die Zuordnung zu entfernen, habe auch versucht, die .ToPagedList zu entfernen, die weiterhin den Fehler auslöst.
André Luiz

Wäre gut, eine vollständige Stapelverfolgung zu sehen
Evk

Und um zu wissen, ob mehrere aktive Ergebnisse aktiviert sind
Jay

Nachdem ich das gleiche Problem hatte, stellte ich fest, dass meine Datenbanktabelle nullbare Ganzzahlen enthielt. Sobald ich die Eigenschaften meines Entitätsmodells so eingestellt hatte, dass sie mit nullbaren Ints übereinstimmen, funktionierte alles, sodass die Nachrichten für mich irreführend waren ...!
AlwaysLearning

Antworten:


87

Ich bin nicht sicher, ob Sie IoC und Dependency Injection verwenden, um Ihren DbContext aufzulösen, wo immer er verwendet wird. Wenn Sie dies tun und native IoC aus .NET Core (oder einem anderen IoC-Container) verwenden und diese Fehlermeldung angezeigt wird, müssen Sie Ihren DbContext als Transient registrieren. Machen

services.AddTransient<MyContext>();

ODER

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

anstatt

services.AddDbContext<MyContext>();

AddDbContext fügt den Kontext als Gültigkeitsbereich hinzu, was bei der Arbeit mit mehreren Threads zu Problemen führen kann.

Auch asynchrone / warten-Operationen können dieses Verhalten verursachen, wenn asynchrone Lambda-Ausdrücke verwendet werden.

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

Die einfache Erklärung dafür ist, dass die DbContextImplementierung nicht threadsicher ist. Mehr dazu lesen Sie hier


1
Wenn ich transient verwende, erhalte ich folgende Verbindungsfehler (geschlossen oder entsorgt) 'OmniService.DataAccess.Models.OmniServiceDbContext'. System.ObjectDisposedException: Auf ein entsorgtes Objekt kann nicht zugegriffen werden. Eine häufige Ursache für diesen Fehler besteht darin, einen Kontext zu entsorgen, der durch Abhängigkeitsinjektion behoben wurde, und später zu versuchen, dieselbe Kontextinstanz an einer anderen Stelle in Ihrer Anwendung zu verwenden. Dies kann auftreten, wenn Sie Dispose () für den Kontext aufrufen oder den Kontext in eine using-Anweisung einschließen. ... Objektname: 'AsyncDisposer'.
David

3
Hallo David! Ich denke, Sie verwenden, Task.Run(async () => context.Set...)ohne darauf zu warten, oder erstellen einen DB-Kontext mit Gültigkeitsbereich, ohne auf das Ergebnis zu warten. Dies bedeutet, dass Ihr Kontext wahrscheinlich bereits beim Zugriff darauf verfügbar ist. Wenn Sie mit Microsoft DI arbeiten, müssen Sie selbst einen Abhängigkeitsbereich erstellen Task.Run. Schauen Sie sich auch diese Links an. stackoverflow.com/questions/45047877/… docs.microsoft.com/en-us/dotnet/api/…
alsami

3
Wie bereits erwähnt, tritt dieses Problem auf, wenn Sie eine asynchrone Methode mit dem Schlüsselwort await nicht aufrufen.
Yennefer

1
Dies kann in Ordnung sein, aber man muss über die gewünschte Lebensdauer und den gewünschten Auflösungsumfang des Datenzugriffs mehr nachdenken als über die unbekümmerte Verwendung von Transienten ohne Nuance der Umstände. In der Tat würde ich es als selten betrachten, dass man einen Datenkontext vorübergehend haben möchte. Wenn der Umfang einer Arbeitseinheit mehr als eine einzelne Datenoperation umfasst, sollte der Transaktionsumfang mehr als diesen umfassen. Die Auflösung Ihres Datenkontexts sollte den Umfang Ihrer Arbeitseinheit widerspiegeln. Dies ist etwas, das durchdacht werden sollte, und dies ist keine einheitliche Antwort.
Dave Rael

3
@alsami du bist mein Held. 6 Stunden schmerzhaftes Debuggen. Dies war die Lösung. Wenn jemand anderes IHttpContextAccessor in den DbContext einfügt und die Ansprüche null sind, ist dies die Lösung. Vielen Dank Mann.
jcmontx

56

In einigen Fällen tritt dieser Fehler auf, wenn eine asynchrone Methode ohne das awaitSchlüsselwort awaitaufgerufen wird. Dies kann einfach durch Hinzufügen vor dem Methodenaufruf behoben werden . Die Antwort bezieht sich möglicherweise nicht auf die genannte Frage, kann jedoch zur Lösung eines ähnlichen Fehlers beitragen.


4
Das ist mir passiert. Wechseln First()zu await / FirstAsync()arbeiten.
Guilherme

Upvote. Siehe auch jimlynn.wordpress.com/2017/11/16/… Jim Lynn "ENTITY FRAMEWORK ERROR: EIN ZWEITER BETRIEB, DER IN DIESEM KONTEXT BEGINNT, BEVOR EIN VORHERIGER BETRIEB ABGESCHLOSSEN WIRD.
GranadaCoder

Danke dafür! Dies war genau mein Problem ... Ich habe vergessen, ein Warten auf eine asynchrone Methode hinzuzufügen.
AxleWack

Ist mir auch passiert, und dieser Kommentar hat geholfen, als ich gesucht habe, wo ich ein fehlendes Warten vergessen habe. Sobald ich es gefunden habe, ist das Problem gelöst.
Zion Hai

42

Die Ausnahme bedeutet, dass _contextzwei Threads gleichzeitig verwendet werden. entweder zwei Threads in derselben Anforderung oder durch zwei Anforderungen.

Ist Ihre _contextdeklarierte statische Aufladung vielleicht? Es sollte nicht sein.

Oder rufen Sie GetClientsin derselben Anfrage mehrmals von einer anderen Stelle in Ihrem Code aus an?

Möglicherweise tun Sie dies bereits, aber im Idealfall verwenden Sie die Abhängigkeitsinjektion für Ihre DbContext, was bedeutet, dass Sie sie AddDbContext()in Ihrer Startup.cs verwenden, und Ihr Controller-Konstruktor sieht ungefähr so ​​aus:

private readonly MyDbContext _context; //not static

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

Wenn Ihr Code nicht so ist, zeigen Sie es uns und vielleicht können wir Ihnen weiterhelfen.


1
Wahrscheinlich ist es der Job, den ich habe. Ich habe es geschafft zu lösen, siehe meine Antwort. Aber ich markiere deine als die richtige
André Luiz

Mein Code ist genau so und wir verfolgen häufig "Eine zweite Operation wurde in diesem Kontext gestartet, bevor eine vorherige asynchrone Operation abgeschlossen wurde. Verwenden Sie" Warten ", um sicherzustellen, dass alle asynchronen Operationen abgeschlossen wurden, bevor Sie eine andere Methode in diesem Kontext aufrufen. Instanzmitglieder sind dies nicht garantiert threadsicher. - bei System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered () ".
NMathur

@NMathur Verwenden Sie Ihr _contextObjekt in anderen Threads? Wie in einem Task.Run()zum Beispiel?
Gabriel Luci

@ GabrielLuci Alle meine Methoden sind asynchron wie unten, wird dies das Problem verursachen. Mein Wissen zu diesem Thema ist sehr gering. Können Sie vorschlagen, wo und was ich im Detail lesen sollte, um diese Verhaltensweisen zu verstehen? public async Task <List <Item>> GetItems (int orderId) {List <Item> items = warte auf _context.Item.Where (x => x.OrderId == orderId) .ToListAsync (); Artikel zurücksenden; }
NMathur

@NMathur Das sieht gut aus. Stellen Sie einfach sicher, dass Sie immer awaitasynchrone Methoden verwenden. Wenn Sie nicht verwenden await, können Sie ungewollt in Multithreading geraten.
Gabriel Luci

8
  • Lösen Sie mein Problem mit dieser Codezeile in meiner Datei Startup.cs.
    Das Hinzufügen eines vorübergehenden Dienstes bedeutet, dass jedes Mal, wenn der Dienst angefordert wird, eine neue Instanz erstellt wird, wenn Sie mit der Abhängigkeitsinjektion arbeiten

           services.AddDbContext<Context>(options =>
                            options.UseSqlServer(_configuration.GetConnectionString("ContextConn")),
                 ServiceLifetime.Transient);
    

6

Ich hatte das gleiche Problem und es stellte sich heraus, dass der Elternservice ein Singelton war. So wurde der Kontext automatisch auch zu Singelton. Obwohl in DI als Per Life Time Scoped deklariert wurde.

Injizieren von Diensten mit unterschiedlichen Lebensdauern in einen anderen

  1. Fügen Sie niemals Scoped & Transient-Dienste in den Singleton-Dienst ein. (Dadurch wird der vorübergehende Dienst oder Dienst mit Gültigkeitsbereich effektiv in den Singleton konvertiert.)

  2. Injizieren Sie niemals vorübergehende Dienste in den Bereichsdienst (Dies wandelt den vorübergehenden Dienst in den Bereich um.)


Das war genau mein Problem
Jonesopolis

Das war auch mein Problem. Ich habe eine Handlerklasse als Singleton und den DbContext als vorübergehend registriert. Ich musste ServiceProvider innerhalb der Handler-Klasse verwenden, um jedes Mal, wenn der Handler geschlagen wird, eine vorübergehende Instanz aus dem DI-Container abzurufen
Daiana Sodré,

5

Ich hatte den gleichen Fehler. Es geschah, weil ich eine Methode aufgerufen habe, die als public async void ...statt konstruiert wurde public async Task ....


4

Ich denke, diese Antwort kann immer noch jemandem helfen und viele Male sparen. Ich habe ein ähnliches Problem gelöst, indem ich IQueryablezu List(oder zu Array, Sammlung ...) gewechselt bin .

Zum Beispiel:

var list=_context.table1.where(...);

zu

var list=_context.table1.where(...).ToList(); //or ToArray()...

2
IMHO, diese Antwort verdient keine Minuspunkte. Sie ist nur schlecht ausgedrückt. .ToList () löst in der Tat die meisten Probleme "eine zweite Operation ...", da die sofortige Auswertung des Ausdrucks erzwungen wird. Auf diese Weise gibt es keine Warteschlangenkontextoperationen.
Vasilag

Dies war das Problem in meinem Fall. Ich hatte xxx.Contains (z.prop) in einer where-Klausel einer Abfrage. xxx sollte ein bestimmtes int [] -Array sein, das aus einer früheren Abfrage aufgelöst wurde. Leider war xxx zum Zeitpunkt der zweiten Abfrage noch ein IQueryable. Das Hinzufügen von xxx.ToArray () vor der zweiten Abfrage hat mein Problem behoben.
Jason Butera

2

Ich hatte das gleiche Problem, aber der Grund war keiner der oben aufgeführten. Ich habe eine Aufgabe erstellt, einen Bereich innerhalb der Aufgabe erstellt und den Container gebeten, einen Dienst zu erhalten. Das hat gut funktioniert, aber dann habe ich einen zweiten Dienst innerhalb der Aufgabe verwendet und vergessen, ihn auch für den neuen Bereich anzufordern. Aus diesem Grund verwendete der 2. 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();

Wäre der Kontext nicht erst verfügbar, wenn alles im using-Block ausgeführt wurde?
Sello Mkantjwa

Wenn die Aktion entsorgt wird, wird alles in diesem Bereich entsorgt. Wenn eine Aufgabe im Hintergrund ausgeführt wird und diese Aufgabe länger als die Aktion ist, tritt dieses Problem auf, es sei denn, Sie erstellen einen neuen Bereich für die Aufgabe, wie ich es im Beispiel getan habe. Wenn Ihre Aufgabe jedoch lange dauern kann oder Sie zu 100% sicher sein möchten, dass sie ausgeführt wird, müssen Sie möglicherweise eine Warteschlange verwenden. Wenn Sie Azure verwenden, können Sie Service Bus-Warteschlangen verwenden.
Francisco Goldenstein

2

Entity Framework Core unterstützt nicht mehrere parallele Vorgänge, die auf derselben DbContextInstanz ausgeführt werden. Dies umfasst sowohl die parallele Ausführung von asyncAbfragen als auch die explizite gleichzeitige Verwendung mehrerer Threads. await asyncRufen Sie daher immer sofort auf oder verwenden Sie separate DbContextInstanzen für Operationen, die parallel ausgeführt werden.


1

Meine Situation ist anders: Ich habe versucht, die Datenbank mit 30 Benutzern zu versehen, die zu bestimmten Rollen gehören, also habe ich diesen Code ausgeführt:

for (var i = 1; i <= 30; i++)
{
    CreateUserWithRole("Analyst", $"analyst{i}", UserManager);
}

Dies war eine Synchronisierungsfunktion. Darin hatte ich 3 Anrufe zu:

UserManager.FindByNameAsync(username).Result
UserManager.CreateAsync(user, pass).Result
UserManager.AddToRoleAsync(user, roleName).Result

Als ich ersetzt .Resultmit .GetAwaiter().GetResult(), ging dieser Fehler weg.


0

Ich habe die gleiche Nachricht erhalten. Aber in meinem Fall macht es keinen Sinn. Mein Problem ist, dass ich versehentlich eine "NotMapped" -Eigenschaft verwendet habe. Dies bedeutet wahrscheinlich nur in einigen Fällen einen Fehler der Linq-Syntax oder der Modellklasse. Die Fehlermeldung scheint irreführend. Die ursprüngliche Bedeutung dieser Nachricht ist, dass Sie in derselben Anforderung nicht mehr als einmal asynchron für denselben Datenbankkontext 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-this-context-before-a-previous-operation-completed


0

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

Eine Lösung, wie in diesem Thread erwähnt, besteht darin, die Lebensdauer des DbContext auf vorübergehend zu ändern, indem Sie ihn wie folgt definieren

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

aber weil ich Änderungen an mehreren verschiedenen Diensten vornehme und sie gleichzeitig mit dem festschreibe 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 so zu nutzen, als wäre es eine einfache Anfrage. Um das Problem zu lösen, habe ich einfach den einzelnen Bereich in zwei Bereiche aufgeteilt, einen für die Abfrage und einen für die Schreibvorgänge wie folgt:

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 ist, bevor die Iteration gestartet wird. Das ist in meinem Fall nicht sehr praktisch, da es viele Ergebnisse geben könnte, die alle für den Vorgang in den Speicher geladen werden müssten, den ich zu vermeiden versuchte, indem ich zuerst eine Abfragbare Datei verwendete.


0

Ich habe es geschafft, diesen Fehler zu erhalten, indem ich eine IQueryablean eine Methode übergeben habe, die diese IQueryable-Liste als Teil einer anderen Abfrage an denselben 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, habe ich hier den Ansatz verwendet und diese Liste materialisiert, bevor ich sie an die zweite Methode übergeben habe, indem ich den Aufruf in " SecondMethodSein" geändert habeSecondMethod(stockItems.ToList()


Dies löste das Problem, verlangsamte aber nicht die Leistung. Gibt es eine alternative Lösung?
Dheeraj Kumar

0

Stimmen Sie zuerst (zumindest) alsamis Antwort ab. Das hat mich auf den richtigen Weg gebracht.

Aber für diejenigen unter Ihnen, die IoC machen, ist hier ein etwas tieferer Tauchgang.

Mein Fehler (wie bei anderen)

Ein oder mehrere Fehler sind aufgetreten. (Eine zweite Operation wurde in diesem Kontext gestartet, bevor eine vorherige Operation abgeschlossen wurde. Dies wird normalerweise durch verschiedene Threads verursacht, die dieselbe Instanz von DbContext verwenden. Weitere Informationen zum Vermeiden von Threading-Problemen mit DbContext finden Sie unter https://go.microsoft.com / fwlink /? linkid = 2097913. )

Mein Code-Setup. "Nur die Grundlagen" ...

public class MyCoolDbContext: DbContext{
    public DbSet <MySpecialObject> MySpecialObjects {        get;        set;    }
}

und

public interface IMySpecialObjectDomainData{}

und (Hinweis MyCoolDbContext wird injiziert)

public class MySpecialObjectEntityFrameworkDomainDataLayer: IMySpecialObjectDomainData{
    public MySpecialObjectEntityFrameworkDomainDataLayer(MyCoolDbContext context) {
        /* HERE IS WHERE TO SET THE BREAK POINT, HOW MANY TIMES IS THIS RUNNING??? */
        this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
    }
}

und

public interface IMySpecialObjectManager{}

und

public class MySpecialObjectManager: IMySpecialObjectManager
{
    public const string ErrorMessageIMySpecialObjectDomainDataIsNull = "IMySpecialObjectDomainData is null";
    private readonly IMySpecialObjectDomainData mySpecialObjectDomainData;

    public MySpecialObjectManager(IMySpecialObjectDomainData mySpecialObjectDomainData) {
        this.mySpecialObjectDomainData = mySpecialObjectDomainData ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectDomainDataIsNull, (Exception)null);
    }
}

Und schließlich meine Multithread-Klasse, die von einer Konsolen-App (Befehlszeilenschnittstellen-App) aufgerufen wird.

    public interface IMySpecialObjectThatSpawnsThreads{}

und

public class MySpecialObjectThatSpawnsThreads: IMySpecialObjectThatSpawnsThreads
{
    public const string ErrorMessageIMySpecialObjectManagerIsNull = "IMySpecialObjectManager is null";

    private readonly IMySpecialObjectManager mySpecialObjectManager;

    public MySpecialObjectThatSpawnsThreads(IMySpecialObjectManager mySpecialObjectManager) {
        this.mySpecialObjectManager = mySpecialObjectManager ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectManagerIsNull, (Exception)null);
    }
}

und der DI-Aufbau. (Auch dies gilt für eine Konsolenanwendung (Befehlszeilenschnittstelle) ... die sich geringfügig von Web-Apps unterscheidet.)

private static IServiceProvider BuildDi(IConfiguration configuration) {
    /* this is being called early inside my command line application ("console application") */

    string defaultConnectionStringValue = string.Empty; /* get this value from configuration */

    ////setup our DI
    IServiceCollection servColl = new ServiceCollection()
        ////.AddLogging(loggingBuilder => loggingBuilder.AddConsole())

        /* THE BELOW TWO ARE THE ONES THAT TRIPPED ME UP.  */
        .AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
    .AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()

    /* so the "ServiceLifetime.Transient" below................is what you will find most commonly on the internet search results */
     # if (MY_ORACLE)
        .AddDbContext<ProvisioningDbContext>(options => options.UseOracle(defaultConnectionStringValue), ServiceLifetime.Transient);
     # endif

     # if (MY_SQL_SERVER)
        .AddDbContext<ProvisioningDbContext>(options => options.UseSqlServer(defaultConnectionStringValue), ServiceLifetime.Transient);
     # endif

    servColl.AddSingleton <IMySpecialObjectThatSpawnsThreads,        MySpecialObjectThatSpawnsThreads>();

    ServiceProvider servProv = servColl.BuildServiceProvider();

    return servProv;
}

Diejenigen, die mich überraschten, waren die (Wechsel zu) vergänglich für

        .AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
    .AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()

Hinweis: Da IMySpecialObjectManager in "MySpecialObjectThatSpawnsThreads" injiziert wurde, mussten diese injizierten Objekte vorübergehend sein, um die Kette zu vervollständigen.

Der Punkt ist ....... es war nicht nur der (My) DbContext, der benötigt wurde .Transient ... sondern ein größerer Teil des DI-Graphen.

Debugging-Tipp:

Diese Linie:

this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);

Setzen Sie dort Ihren Debugger-Haltepunkt. Wenn Ihr MySpecialObjectThatSpawnsThreads N Threads erstellt (z. B. 10 Threads) ... und diese Zeile nur einmal getroffen wird ... ist das Ihr Problem. Ihr DbContext kreuzt Threads.

BONUS:

Ich würde vorschlagen, diese unten stehende URL / Artikel (Oldie but Goodie) über die Unterschiede zwischen Web-Apps und Konsolen-Apps zu lesen

https://mehdi.me/ambient-dbcontext-in-ef6/

Hier ist die Kopfzeile des Artikels, falls sich der Link ändert.

DBCONTEXT MIT ENTITY FRAMEWORK 6 RICHTIG VERWALTEN: EIN TIEFENFÜHRER Mehdi El Gueddari

Ich habe dieses Problem mit WorkFlowCore festgestellt https://github.com/danielgerlag/workflow-core

  <ItemGroup>
    <PackageReference Include="WorkflowCore" Version="3.1.5" />
  </ItemGroup>

Beispielcode unten .. um zukünftigen Internet-Suchern zu helfen

 namespace MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Workflows
    {
        using System;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Constants;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Glue;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.WorkflowSteps;

        using WorkflowCore.Interface;
        using WorkflowCore.Models;

        public class MySpecialObjectInterviewDefaultWorkflow : IWorkflow<MySpecialObjectInterviewPassThroughData>
        {
            public const string WorkFlowId = "MySpecialObjectInterviewWorkflowId";

            public const int WorkFlowVersion = 1;

            public string Id => WorkFlowId;

            public int Version => WorkFlowVersion;

            public void Build(IWorkflowBuilder<MySpecialObjectInterviewPassThroughData> builder)
            {
                builder
                             .StartWith(context =>
                    {
                        Console.WriteLine("Starting workflow...");
                        return ExecutionResult.Next();
                    })

                        /* bunch of other Steps here that were using IMySpecialObjectManager.. here is where my DbContext was getting cross-threaded */


                    .Then(lastContext =>
                    {
                        Console.WriteLine();

                        bool wroteConcreteMsg = false;
                        if (null != lastContext && null != lastContext.Workflow && null != lastContext.Workflow.Data)
                        {
                            MySpecialObjectInterviewPassThroughData castItem = lastContext.Workflow.Data as MySpecialObjectInterviewPassThroughData;
                            if (null != castItem)
                            {
                                Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete :)  {0}   -> {1}", castItem.PropertyOne, castItem.PropertyTwo);
                                wroteConcreteMsg = true;
                            }
                        }

                        if (!wroteConcreteMsg)
                        {
                            Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete (.Data did not cast)");
                        }

                        return ExecutionResult.Next();
                    }))

                    .OnError(WorkflowCore.Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(60));

            }
        }
    }

und

ICollection<string> workFlowGeneratedIds = new List<string>();
                for (int i = 0; i < 10; i++)
                {
                    MySpecialObjectInterviewPassThroughData currentMySpecialObjectInterviewPassThroughData = new MySpecialObjectInterviewPassThroughData();
                    currentMySpecialObjectInterviewPassThroughData.MySpecialObjectInterviewPassThroughDataSurrogateKey = i;

                    ////  private readonly IWorkflowHost workflowHost;
                    string wfid = await this.workflowHost.StartWorkflow(MySpecialObjectInterviewDefaultWorkflow.WorkFlowId, MySpecialObjectInterviewDefaultWorkflow.WorkFlowVersion, currentMySpecialObjectInterviewPassThroughData);
                    workFlowGeneratedIds.Add(wfid);
                }

0

In meinem Fall verwende ich eine Vorlagenkomponente in Blazor.

 <BTable ID="Table1" TotalRows="MyList.Count()">

Das Problem besteht darin, eine Methode (Count) im Komponentenheader aufzurufen. Um das Problem zu lösen, habe ich es folgendermaßen geändert:

int total = MyList.Count();

und später :

<BTable ID="Table1" TotalRows="total">

0

Ich weiß, dass dieses Problem vor zwei Jahren gestellt wurde, aber ich hatte gerade dieses Problem und das von mir verwendete Update hat wirklich geholfen.

Wenn Sie zwei Abfragen mit demselben Kontext ausführen, müssen Sie möglicherweise die entfernen AsNoTracking. Wenn Sie verwenden AsNoTracking, erstellen Sie für jeden Lesevorgang einen neuen Datenleser. Zwei Datenleser können nicht dieselben Daten lesen.


0

In meinem Fall habe ich eine Sperre verwendet, die die Verwendung von "Warten" nicht zulässt und keine Compiler-Warnung erstellt, wenn Sie nicht auf eine asynchrone Funktion warten.

Das Problem:

lock (someLockObject) {
    // do stuff
    context.SaveChangesAsync();
}

// some other code somewhere else doing await context.SaveChangesAsync() shortly after the lock gets the concurrency error

Die Lösung: Warten Sie auf die Asynchronisierung im Schloss, indem Sie sie mit .Wait () blockieren.

lock (someLockObject) {
    // do stuff
    context.SaveChangesAsync().Wait();
}

0

Ein weiterer möglicher Fall: Wenn Sie die Verbindung direkt verwenden, vergessen Sie nicht, if zu schließen. Ich musste eine beliebige SQL-Abfrage ausführen und das Ergebnis lesen. Dies war eine schnelle Lösung. Ich wollte keine Datenklasse definieren und keine "normale" SQL-Verbindung einrichten. Also habe ich einfach die Datenbankverbindung von EFC als wiederverwendet var connection = Context.Database.GetDbConnection() as SqlConnection. Stellen Sie sicher, dass Sie anrufen, connection.Close()bevor Sie dies tun Context.SaveChanges().


-1

Ich habe das gleiche Problem, wenn ich versuche, FirstOrDefaultAsync() die asynchrone Methode im folgenden Code zu verwenden. Und als ich FirstOrDefault()das Problem behoben habe, war das Problem gelöst!

_context.Issues.Add(issue);
        await _context.SaveChangesAsync();

        int userId = _context.Users
            .Where(u => u.UserName == Options.UserName)
            .FirstOrDefaultAsync()
            .Id;
...

1
Es ist überhaupt nicht mit FirstOrDefault () oder FirstOrDefaultAsync () verwandt. Es geht um die Verwendung von dbContext.
Sajadre

-2

Wenn Ihre Methode etwas zurückgibt, können Sie diesen Fehler beheben .Result, indem Sie das Ende des Jobs beenden und .Wait()nichts zurückgeben.


-6

Ich habe es gerade geschafft, dass es wieder funktioniert. Es macht nicht viel Sinn, aber es hat funktioniert:

  1. Entfernen Sie Hangfire aus StartUp (ich habe dort meinen Job erstellt)
  2. Die Hangfire-Datenbank wurde gelöscht
  3. Starten Sie den Server neu

Ich werde später weiter nachforschen, aber die Methode, die ich mit Hangfire aufgerufen habe, erhält einen DBContext und das ist die mögliche Ursache.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.