Entity Framework Core: druga operacja rozpoczęła się w tym kontekście przed zakończeniem poprzedniej operacji

88

Pracuję nad projektem ASP.Net Core 2.0 przy użyciu 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"/>

W jednej z moich metod listy otrzymuję ten błąd:

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

To jest moja metoda:

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

Jestem trochę zagubiony, zwłaszcza, że ​​działa, gdy uruchamiam go lokalnie, ale kiedy wdrażam na moim serwerze przejściowym (IIS 8.5), pojawia się ten błąd i działa normalnie. Błąd zaczął się pojawiać po zwiększeniu maksymalnej długości jednego z moich modeli. Zaktualizowałem również maksymalną długość odpowiedniego modelu widoku. Istnieje wiele innych metod tworzenia list, które są bardzo podobne i działają.

Miałem uruchomioną pracę Hangfire, ale ta praca nie używa tej samej jednostki. To wszystko, co uważam za istotne. Jakieś pomysły, co może to powodować?

André Luiz
źródło
1
Sprawdź to .
Berkay,
2
@Berkay Widziałem to i wiele innych podobnych pytań i wypróbowałem je. Moja metoda była asynchroniczna i zsynchronizowałem ją, aby uniknąć tych problemów. Próbuję też usunąć mapowanie, próbowałem też usunąć .ToPagedList dalej wyrzuca błąd.
André Luiz
Byłoby dobrze,
gdybyśmy
I wiedzieć, czy włączonych jest wiele aktywnych wyników
Jay
Mając ten sam problem, odkryłem, że w mojej tabeli bazy danych znajdują się liczby całkowite dopuszczające wartość zerową. Gdy tylko ustawiłem właściwości modelu jednostki na zgodne wartości null, wszystko zaczęło działać, więc komunikaty wprowadzały mnie w błąd ...!
AlwaysLearning

Odpowiedzi:

87

Nie jestem pewien, czy używasz IoC i Dependency Injection, aby rozwiązać swój kontekst DbContext, gdziekolwiek może być używany. Jeśli to zrobisz i używasz natywnego IoC z .NET Core (lub dowolnego innego kontenera IoC) i otrzymujesz ten błąd, pamiętaj, aby zarejestrować swój DbContext jako przejściowy. Zrobić

services.AddTransient<MyContext>();

LUB

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

zamiast

services.AddDbContext<MyContext>();

AddDbContext dodaje kontekst jako określony w zakresie, co może powodować problemy podczas pracy z wieloma wątkami.

Również operacje async / await mogą powodować to zachowanie w przypadku korzystania z wyrażeń lambda asynchronicznego.

Dodanie go jako przejściowego ma również swoje wady. Nie będzie można wprowadzać zmian w niektórych jednostkach w wielu klasach używających kontekstu, ponieważ każda klasa otrzyma własne wystąpienie DbContext.

Prostym wytłumaczeniem jest to, że DbContextimplementacja nie jest bezpieczna wątkowo. Możesz przeczytać więcej na ten temat tutaj

alsami
źródło
1
Kiedy używam przejściowego, otrzymuję następujące błędy połączenia (zamknięte lub usunięte) „OmniService.DataAccess.Models.OmniServiceDbContext”. System.ObjectDisposedException: nie można uzyskać dostępu do usuniętego obiektu. Typową przyczyną tego błędu jest usunięcie kontekstu, który został rozwiązany z wstrzyknięcia zależności, a następnie próba użycia tego samego wystąpienia kontekstu w innym miejscu w aplikacji. Może się tak zdarzyć, jeśli wywołujesz metodę Dispose () w kontekście lub zawijasz kontekst instrukcją using. ... Nazwa obiektu: „AsyncDisposer”.
David
3
Cześć David! Myślę, że używasz Task.Run(async () => context.Set...)bez czekania na to lub tworzenia ograniczonego kontekstu db bez oczekiwania na wynik. Oznacza to, że Twój kontekst jest prawdopodobnie już usunięty podczas uzyskiwania do niego dostępu. Jeśli korzystasz z Microsoft DI, musisz samodzielnie utworzyć zakres zależności w tym Task.Run. Sprawdź również te linki. stackoverflow.com/questions/45047877/ ... docs.microsoft.com/en-us/dotnet/api/ ...
alsami
3
Jak wspomniano wcześniej, jeśli pominiesz wywołanie metody asynchronicznej ze słowem kluczowym await, napotkasz ten problem.
Yennefer,
To mi pomogło. Wstrzykuję zależności do nowego wątku z usługi hostowanej, gdy otrzymamy nową wiadomość z kolejki. Zmiana kontekstu naprawiła wyjątek
Cale,
3
@alsami, jesteś moim bohaterem. 6 godzin bolesnego debugowania. To było rozwiązanie. Jeśli ktoś inny wstrzykuje IHttpContextAccessor do DbContext, a Claims są null, to jest rozwiązanie. Bardzo dziękuję człowieku.
jcmontx
55

W niektórych przypadkach ten błąd występuje podczas wywoływania metody asynchronicznej bez awaitsłowa kluczowego, co można po prostu rozwiązać, dodając awaitprzed wywołaniem metody. jednak odpowiedź może nie być związana z wymienionym pytaniem, ale może pomóc w rozwiązaniu podobnego błędu.

Hamid Nasirloo
źródło
4
To mi się przydarzyło. Zmiana First()na await / FirstAsync()pracę.
Guilherme
Głosuj za. Zobacz także jimlynn.wordpress.com/2017/11/16/… Jim Lynn „BŁĄD RAMY JEDNOSTKI: DRUGA OPERACJA ROZPOCZĘŁA SIĘ W TYM KONTEKŚCIE PRZED UKOŃCZENIEM POPRZEDNIEJ OPERACJI. ŻADEN CZŁONKOWIE INSTANCJI NIE SĄ GWARANTOWANI, ŻE BĘDĄ BEZPIECZNI.”.
granadaCoder
Dzięki za to! To był dokładnie mój problem ... Zapomniałem dodać await na asynchronicznej metodzie.
AxleWack
Mnie też się zdarzyło, a ten komentarz pomógł, gdy szukałem miejsca, w którym zapomniałem o brakującym oczekiwaniu. Kiedy go znalazłem, problem został rozwiązany.
Zion Hai
42

Wyjątek oznacza, że _contextjest używany przez dwa wątki w tym samym czasie; albo dwa wątki w tym samym żądaniu, albo przez dwa żądania.

Czy Twoja _contextdeklarowana statyczność może być? Nie powinno być.

A może dzwonisz GetClientswiele razy w ramach tego samego żądania z innego miejsca w swoim kodzie?

Być może już to robisz, ale idealnie byłoby, gdybyś używał iniekcji zależności dla swojego DbContext, co oznacza, że ​​będziesz używać AddDbContext()w swoim Startup.cs, a konstruktor kontrolera będzie wyglądał mniej więcej tak:

private readonly MyDbContext _context; //not static

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

Jeśli Twój kod nie jest taki, pokaż nam, a może pomożemy Ci dalej.

Gabriel Luci
źródło
1
Prawdopodobnie to moja praca. Udało mi się rozwiązać, zobacz moją odpowiedź. Ale oznaczam twój jako właściwy
André Luiz
Mój kod jest dokładnie taki, jak ten i często śledzimy „Druga operacja rozpoczęta w tym kontekście przed zakończeniem poprzedniej operacji asynchronicznej. Użyj„ await ”, aby upewnić się, że wszystkie operacje asynchroniczne zostały zakończone przed wywołaniem innej metody w tym kontekście. Żadne elementy członkowskie instancji nie są gwarancja bezpieczeństwa wątków. - at System.Data.Entity.Internal.ThrowingMonitor.EnsureNotEntered () ".
NMathur
@NMathur Czy używasz swojego _contextobiektu w innych wątkach? Jak Task.Run()na przykład w środku ?
Gabriel Luci
@GabrielLuci wszystkie moje metody są asynchroniczne, jak poniżej, czy spowoduje to problem. Moja wiedza na ten temat jest bardzo niewielka. Czy możesz zasugerować, gdzie i co powinienem szczegółowo przeczytać, aby zrozumieć te zachowania? public async Task <List <Item>> GetItems (int orderId) {List <Item> items = await _context.Item.Where (x => x.OrderId == orderId) .ToListAsync (); zwrócić przedmioty; }
NMathur
@NMathur Wygląda dobrze. Upewnij się tylko, że zawsze korzystasz awaitz metod asynchronicznych. Jeśli nie używasz await, możesz przypadkowo włączyć się w wielowątkowość.
Gabriel Luci
8
  • Rozwiąż mój problem, używając tej linii kodu w moim pliku Startup.cs.
    Dodanie usługi przejściowej oznacza, że ​​za każdym razem, gdy usługa jest żądana, tworzone jest nowe wystąpienie podczas pracy z iniekcją zależności

           services.AddDbContext<Context>(options =>
                            options.UseSqlServer(_configuration.GetConnectionString("ContextConn")),
                 ServiceLifetime.Transient);
    
Muhammad Armaghan
źródło
6

Miałem ten sam problem i okazało się, że usługa rodzicielska to singelton. Zatem kontekst automatycznie stał się również singeltonowy. Mimo że został zadeklarowany jako Per Life Time Scoped w DI.

Wstrzykiwanie usługi o różnych okresach życia do innej

  1. Nigdy nie wprowadzaj usług Scoped & Transient do usługi Singleton. (To skutecznie konwertuje usługę przejściową lub o określonym zakresie na singleton).

  2. Nigdy nie wstrzykuj usług przejściowych do usługi w zakresie (to konwertuje usługę przejściową do zakresu).

DiPix
źródło
to był dokładnie mój problem
Jonesopolis
To też był mój problem. Rejestrowałem klasę obsługi jako singleton, a DbContext jako przejściową. Musiałem użyć ServiceProvider w klasie Handler dostać przemijające wystąpienie z każdym razem kontenera DI Handler jest hitted
DAIANA Sodre
5

Miałem ten sam błąd. Stało się tak, ponieważ wywołałem metodę, która została skonstruowana jako public async void ...zamiast public async Task ....

Raynlaze
źródło
4

Myślę, że ta odpowiedź może komuś pomóc i wiele razy uratować. Rozwiązałem podobny problem, zmieniając IQueryablena List(lub na tablicę, kolekcję ...).

Na przykład:

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

do

var list=_context.table1.where(...).ToList(); //or ToArray()...
Makhidi Masimov
źródło
2
IMHO, ta odpowiedź nie zasługuje na punkty ujemne, jest po prostu słabo wyrażona. .ToList () rzeczywiście rozwiązuje większość problemów „druga operacja ...”, ponieważ wymusza natychmiastową ocenę wyrażenia. W ten sposób nie ma kolejkowania operacji kontekstowych.
vassilag
To był problem w moim przypadku. Miałem xxx.Contains (z.prop) w klauzuli Where zapytania. xxx miał być odrębną tablicą int [] rozwiązaną z wcześniejszego zapytania. Niestety, w momencie, gdy trafiło drugie zapytanie, xxx nadal był IQueryable. Dodanie xxx.ToArray () przed drugim zapytaniem rozwiązało mój problem.
Jason Butera
2

Napotkałem ten sam problem, ale powodem nie był żaden z wymienionych powyżej. Utworzyłem zadanie, utworzyłem zakres w zadaniu i poprosiłem kontener o uzyskanie usługi. To działało dobrze, ale potem użyłem drugiej usługi w zadaniu i zapomniałem również poprosić o to do nowego zakresu. Z tego powodu druga usługa korzystała z DbContext, który został już usunięty.

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

Powinienem był to zrobić:

var otherProductService = scope.ServiceProvider.GetService<IProductService>();
otherProductService.DoSomething();
Francisco Goldenstein
źródło
Czy kontekst nie zostałby ujawniony dopiero po zakończeniu wykonywania wszystkiego w bloku using?
Sello Mkantjwa
Kiedy akcja zostanie usunięta, wszystko zostanie usunięte w tym zakresie. Jeśli masz zadanie działające w tle, a to zadanie jest dłuższe niż czynność, problem wystąpi, chyba że utworzysz nowy zakres dla zadania, tak jak to zrobiłem w przykładzie. Z drugiej strony, jeśli Twoje zadanie może zająć dużo czasu lub chcesz mieć 100% pewności, że zostanie uruchomione, może być konieczne użycie kolejki. Jeśli używasz platformy Azure, możesz użyć kolejek usługi Service Bus.
Francisco Goldenstein
2

Entity Framework Core nie obsługuje wielu równoległych operacji uruchamianych w tym samym DbContextwystąpieniu. Obejmuje to zarówno równoległe wykonywanie asynczapytań, jak i dowolne jawne współbieżne użycie z wielu wątków. Dlatego zawsze await asyncwywołuje natychmiast lub używaj oddzielnych DbContextwystąpień dla operacji wykonywanych równolegle.

Ehsan Gondal
źródło
1

Moja sytuacja jest inna: próbowałem zalać bazę danych 30 użytkownikami należącymi do określonych ról, więc uruchomiłem ten kod:

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

To była funkcja synchronizacji. Wewnątrz miałem 3 telefony do:

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

Kiedy otrzymuje .Resultsię .GetAwaiter().GetResult()ten błąd odszedł.

Catalin
źródło
0

Otrzymałem tę samą wiadomość. Ale w moim przypadku to nie ma sensu. Mój problem polega na tym, że przez pomyłkę użyłem właściwości „NotMapped”. Prawdopodobnie oznacza to tylko błąd składni Linq lub klasy modelu w niektórych przypadkach. Komunikat o błędzie wydaje się mylący. Pierwotne znaczenie tego komunikatu polega na tym, że nie można wywołać async w tym samym kontekście dbcontext więcej niż raz w tym samym żądaniu.

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

Możesz sprawdzić ten link, aby uzyskać szczegółowe informacje, https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-this-context-before-a-previous-operation-completed

Sharon Zhou
źródło
0

Mam usługę w tle, która wykonuje akcję dla każdego wpisu w tabeli. Problem polega na tym, że jeśli iteruję i modyfikuję niektóre dane w tym samym wystąpieniu DbContext, ten błąd występuje.

Jednym z rozwiązań, jak wspomniano w tym wątku, jest zmiana czasu życia DbContext na przejściowy przez zdefiniowanie go tak, jak

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

ale ponieważ robię zmiany w wielu różnych usługach i zatwierdzam je na raz przy użyciu SaveChanges()metody, to rozwiązanie nie działa w moim przypadku.

Ponieważ mój kod działa w usłudze, robiłem coś takiego

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

aby móc korzystać z usługi tak, jakby to była prosta prośba. Aby rozwiązać ten problem, po prostu podzielę pojedynczy zakres na dwa, jeden dla zapytania, a drugi dla operacji zapisu, takich jak:

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

W ten sposób istnieją efektywnie dwa różne wystąpienia używanego DbContext.

Innym możliwym rozwiązaniem byłoby upewnienie się, że operacja odczytu została zakończona przed rozpoczęciem iteracji. W moim przypadku nie jest to zbyt praktyczne, ponieważ może być wiele wyników, które musiałyby zostać załadowane do pamięci dla operacji, której starałem się uniknąć, używając w pierwszej kolejności Queryable.

NiPfi
źródło
0

Udało mi się uzyskać ten błąd, przekazując metodę IQueryabledo metody, która następnie używała tej „listy” IQueryable jako części innego zapytania w tym samym kontekście.

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

Aby zatrzymać że dzieje użyłem podejście tutaj i zmaterializował ten wykaz przed przekazaniem go drugą metodę, zmieniając wezwanie do SecondMethodbyćSecondMethod(stockItems.ToList()

tomRedox
źródło
To rozwiązało problem, ale czy to nie spowolni wydajności. Czy jest jakieś alternatywne rozwiązanie?
Dheeraj Kumar
0

Najpierw zagłosuj za (przynajmniej) odpowiedź alsami. To zaprowadziło mnie na właściwą ścieżkę.

Ale dla tych z was, którzy zajmują się IoC, tutaj jest trochę głębsze nurkowanie.

Mój błąd (taki sam jak inne)

Wystąpił jeden lub więcej błędów. (Druga operacja została uruchomiona w tym kontekście przed zakończeniem poprzedniej operacji. Jest to zwykle spowodowane przez różne wątki korzystające z tego samego wystąpienia DbContext. Aby uzyskać więcej informacji na temat unikania problemów z wątkami w przypadku DbContext, zobacz https://go.microsoft.com / fwlink /? linkid = 2097913. )

Moja konfiguracja kodu. „Tylko podstawy” ...

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

i

public interface IMySpecialObjectDomainData{}

i (uwaga MyCoolDbContext jest wstrzykiwany)

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

i

public interface IMySpecialObjectManager{}

i

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

I wreszcie moja wielowątkowa klasa wywoływana z aplikacji konsoli (aplikacja interfejsu wiersza poleceń)

    public interface IMySpecialObjectThatSpawnsThreads{}

i

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

i budowanie DI. (Ponownie dotyczy to aplikacji konsoli (interfejsu wiersza poleceń) ... która wykazuje nieco inne zachowanie niż aplikacje internetowe)

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

Te, które mnie zaskoczyły, to (zmiana na) przejściowe dla

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

Uwaga, myślę, że ponieważ IMySpecialObjectManager był wstrzykiwany do „MySpecialObjectThatSpawnsThreads”, te wstrzyknięte obiekty musiały być przejściowe, aby zakończyć łańcuch.

Chodziło o to, że ....... potrzebował nie tylko (My) DbContext .Transient ... ale większy fragment Di Graph.

Wskazówka dotycząca debugowania:

Ta linia:

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

Umieść tam punkt przerwania debugera. Jeśli Twój MySpecialObjectThatSpawnsThreads tworzy N liczbę wątków (na przykład 10 wątków) ...... i ta linia jest trafiona tylko raz ... to jest twój problem. Twój DbContext przecina wątki.

PREMIA:

Proponuję przeczytać poniższy adres url / artykuł (stary, ale dobry) o różnicach między aplikacjami internetowymi i aplikacjami konsolowymi

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

Oto nagłówek artykułu na wypadek zmiany linku.

WŁAŚCIWE ZARZĄDZANIE DBCONTEXT DZIĘKI RAMOM PODMIOTU 6: PRZEWODNIK POGŁĘBIONY Mehdi El Gueddari

Trafiłem na ten problem z WorkFlowCore https://github.com/danielgerlag/workflow-core

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

przykładowy kod poniżej .. aby pomóc przyszłym wyszukiwarkom internetowym

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

            }
        }
    }

i

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);
                }
granadaCoder
źródło
0

W moim przypadku używam komponentu szablonu w Blazor.

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

Problem polega na wywołaniu metody (Count) w nagłówku komponentu. Aby rozwiązać problem, zmieniłem to w następujący sposób:

int total = MyList.Count();

i później :

<BTable ID="Table1" TotalRows="total">
Ali Borjian
źródło
0

Wiem, że ten problem został zadany dwa lata temu, ale właśnie miałem ten problem i poprawka, której użyłem, naprawdę pomogła.

Jeśli wykonujesz dwa zapytania z tym samym kontekstem - może być konieczne usunięcie AsNoTracking. Jeśli używasz AsNoTracking, tworzysz nowy czytnik danych dla każdego odczytu. Dwa czytniki danych nie mogą odczytać tych samych danych.

Cornelis de Jager
źródło
0

W moim przypadku użyłem blokady, która nie pozwala na użycie await i nie tworzy ostrzeżenia kompilatora, gdy nie czekasz na asynchroniczność.

Problem:

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

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

Poprawka: poczekaj na asynchronizację wewnątrz zamka, blokując ją za pomocą .Wait ()

lock (someLockObject) {
    // do stuff
    context.SaveChangesAsync().Wait();
}
Tikall
źródło
0

Inny możliwy przypadek: jeśli korzystasz z połączenia bezpośredniego, nie zapomnij zamknąć if. Musiałem wykonać dowolne zapytanie SQL i odczytać wynik. To była szybka naprawa, nie chciałem definiować klasy danych, nie ustawiać „normalnego” połączenia SQL. Po prostu ponownie użyłem połączenia z bazą danych EFC jako var connection = Context.Database.GetDbConnection() as SqlConnection. Pamiętaj, aby zadzwonić, connection.Close()zanim to zrobisz Context.SaveChanges().

klenium
źródło
-1

Mam ten sam problem, gdy próbuję użyć FirstOrDefaultAsync() metody asynchronicznej w poniższym kodzie. A kiedy naprawiłemFirstOrDefault() - problem został rozwiązany!

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

        int userId = _context.Users
            .Where(u => u.UserName == Options.UserName)
            .FirstOrDefaultAsync()
            .Id;
...
JuSik404
źródło
1
W ogóle nie jest powiązany z FirstOrDefault () ani FirstOrDefaultAsync (), ale dotyczy użycia dbContext.
sajadre
-2

Jeśli Twoja metoda zwraca coś z powrotem, możesz rozwiązać ten błąd, umieszczając .Resultna końcu zadania i .Wait()jeśli nic nie zwraca.

Hasan_H
źródło
-6

Po prostu udało mi się sprawić, że znowu zadziała. Nie ma to większego sensu, ale zadziałało:

  1. Usuń Hangfire ze StartUp (tworzyłem tam swoją pracę)
  2. Usunięto bazę danych Hangfire
  3. Zrestartowałem serwer

Zbadam dalej, ale metoda, którą wywołałem z hangfire, otrzymuje DBContext i to jest możliwa przyczyna.

André Luiz
źródło