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ć?


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


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.


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.


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

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


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


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

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

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.


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


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


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.


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


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

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

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.


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

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


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

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.


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.