0
votes

Le premier appel de dbContext gèle une application après chaque redémarrage de l'application

Nous avons une application Test WPF (.NET CORE 3.0) avec un seul bouton au centre de l'écran. Le bouton dispose d'un événement de clic et d'une méthode de traitement: xxx

Cette méthode est asynchrone, nous attendons donc que l'interface utilisateur ne gènera pas après le clic. Mais si nous démarrons de l'application et que vous cliquez sur le bouton, l'application gèle pendant 1-2 secondes. Si nous ferons un autre clic, cela fonctionne comme prévu sans gel.

Après cela, nous redémarrons notre application et la situation reviennent.

    La base de données
  1. a été créée
  2. .NET Framework fonctionne exactement le même
  3. dbcontext: XXX

  4. attendre la tâche.delay (5000); travailler normalement

  5. Emballage de ces actions dans l'attente de tâches.Run (...) Normalement

    Pourquoi DBContext gèle Freeze WPF Application d'un premier clic d'abord après le redémarrage?


5 commentaires

Ajouter ASYNC / Await ne rend pas votre application asynchrone. Et il gèle parce qu'il attend jusqu'à ce que l'opération soit terminée et toujours en cours d'exécution dans le fil de l'interface utilisateur


@Olegi 4 et 5 fonctionnent comme prévu. Attendez que ces opérations ne doivent pas bloquer le fil de l'interface utilisateur. Donc, ils ne le font pas. Mais pourquoi le contexte fait?


Essayer d'ajouter .configureawait (false) à vos tâches, cela pourrait résoudre le problème


@Olegi n'a pas travaillé


Une chose qui pourrait se produire ... (Je ne suis pas sûr que ce soit) est que la première fois que l'application WPF tente de créer un thread pour exécuter cela qui prend du temps, car la création de threads est chère.


4 Réponses :


0
votes

Essayez de mettre un point de rupture et de passer à travers chaque ligne.

Je suppose que c'est la ligne qui bloque votre UI est var context = nouveau asyncdbcontext () qui est toujours exécuté en tant que code synchrone. Ma suggestion serait de configurer votre contexte au démarrage de l'application, puis de réutiliser le même instance et de le disposer uniquement à la fermeture de l'application.

Le Net Core DI pourrait aider avec cela. Ou vous pouvez utiliser la bibliothèque IOO que vous préférez

La raison pour laquelle elle ne gèle pas la deuxième fois est parce que dBContext cacherait certaines choses, qui ont pris une Lors de l'initialisation de la première fois

La méthode alternative (rapide et sale) serait d'envelopper le constructeur de contexte dans une tâche .Run appel: xxx < / pré>


14 commentaires

Premièrement, async vide est normal et le seul moyen de travailler avec des événements de base dans WPF, car chaque événement basé sur le délégué, et presque tous les délégués dans WPF ont un type de retour nul. Deuxièmement, utilisez une seule instance de DBContext n'est pas une bonne idée, si l'application peut fonctionner de nombreuses heures ou plusieurs jours. Ce n'est pas une application de production, et je veux comprendre seulement pourquoi il wotks comme ça?


@Olegskidan Bon point, je n'ai pas remarque la signature de la méthode.


@Olegskidan Probablement c'est la raison pour laquelle il fonctionne de manière synchrone? Étant donné que la méthode renvoie le vide, il ne peut pas être attendu par appelant. Et seulement tourner une nouvelle tâche à l'intérieur de cette méthode peut résoudre le problème


@Olegi mais chaque méthode avec "Async" - nous retourne une tâche, n'est-ce pas?


@Olegskidan Dans votre cas, il est nul. Donc, cela ne retourne pas la tâche. Ainsi, cela ne peut pas être attendu. Donc, l'appelant fonctionne de manière synchrone. Le mot ASYNC ne vous donne que la possibilité d'attendre dans la méthode, il ne fait rien d'autre que ceci


@Olegi qui n'est pas vrai. C'est 100% faux. Les méthodes asynchronisées qui renvoient le vide sont asynchrones ... non synchronisées. docs.microsoft.com/en-us/archive/msdn-magazine/2013/march/...


@Olegi Si j'ai changé mon appel de contexte vers async Task.Delay (5000) puis le fil d'interface utilisateur ne gèle pas. Mais la méthode reste asynchrone vide =)


Microsoft a permis à Microsoft a permis d'annuler des méthodes d'annulation asynchronisées dans le but de créer des gestionnaires d'événements ASYNC ......


Mettez simplement, la ligne qui bloque est toujours exécutée exactement comme si la méthode aurait été Void privé (sans aucune asynchronisation dans la signature). Pour résoudre ce problème, vous devrez trouver un moyen de construire le contexte de manière asynchrone ou de la construire une fois, puis de la réutiliser.


@Alex peut-être. Parce que si j'utilise dbcontext dans l'App.xaml Class (point de départ de l'application), il ne gèle qu'une seule fois (au début), mais ce n'est pas la réponse à ma question - pourquoi? Peut-être que le premier appel de dbcontext tente de créer httpconnection? Si oui, qu'il essaie de le créer et de l'ouvrir de manière synchrone? Fermera-t-il après quelques minutes / heures? Cette situation pourrait-elle arriver dans le noyau webapi? =)


@Alex Votre nouvelle suggestion a été remarquée par ma question (tâche.run)


@Onegskidan c'est beaucoup de questions. Le point principal est cependant que le premier appel à un constructeur DBContext prend des moments qui est perceptible, car le thread principal ne peut plus traiter le travail de l'interface utilisateur. plus à ce sujet dans ce fil: Stackoverflow.com/Questtions/44237272/...


@Olegskidan Pas besoin d'envelopper le tout, juste le constructeur. Ou vous pouvez déplacer cela à une méthode d'usine


@Alex di non travaillé aussi. C'est plus rapide, mais pas assez.



0
votes
private async void CreateUsers(object sender, RoutedEventArgs e)
{
  Task.Factory.StartNew(()=>{
    using (var context = new AsyncDbContext())
    {
        await context.Users.AddRangeAsync(new List<User>
            {
                 new User{Name="1"},
                 new User{Name="2"},
                 new User{Name="3"},
                 new User{Name="4"},
                 new User{Name="5"},
                 new User{Name="6"},
                 new User{Name="7"},
                 new User{Name="8"},
            });
        await context.SaveChangesAsync();
        MessageBox.Show("Done!");
});
}

1 commentaires

C'est la pire solution =) Nous n'avons pas besoin de créer une tâche détaillée avec des options, tant mieux d'utiliser la tâche.Run. Et aussi ce n'est pas une réponse sur ma question "Pourquoi?"



0
votes

J'ai lu des réponses ici, et j'aimerais vous parler de quelques rencontres que j'ai eues avec quelque chose qui semble similaire. Je doute que ce sera votre réponse, mais cela peut vous aider, sans distinction, je pense que c'est bien de poster.

2 choses:

  1. Lorsque vous appelez une tâche directement, par ex. 'Asyn onefunc ()' Cela utilisera l'actuel synchronisationContext pour planifier la tâche. Dans WPF, cela passera par défaut au répartiteur . Cela envoiera la tâche au fil principal de l'UI. En d'autres termes, si vous démarrez une tâche et qu'il présente une prise de 5 secondes, votre fil d'interface utilisateur sera programmé avec le travail avec une prise de 5 secondes et vous remarquerez donc un gel d'interface utilisateur de 5 secondes. Vous pouvez plutôt choisir de planifier cela sur le threadpool à l'aide de la tâche .Run . Assurez-vous d'essayer de tester vos tâches lorsque vos tâches font quand et quoi sur quel fil, car cela pourrait être délicat. Tenez également note du contexte de synchronisation.

  2. Lorsque vous utilisez EF6 (cela peut différer de votre version), le premier appel de la base de données initialisera la base de données. Cela peut, selon votre dbcontext, prenez du temps. J'ai également eu un cas où 2 threads feraient «le premier» appelerait le même moment, ce qui provoque la multiplicative du démarrage initial (par exemple, à partir de 2 secondes avec 1 fil à 4 avec 2). < / li>

    Je me sens comme le problème que vous rencontrez est une combinaison de ci-dessus, mais soyez conscient car mon expérience est à propos de .NET 4.8 et EF6 et vous utilisez le succès de ces deux.


1 commentaires

Encore une fois, je ne sais pas si cela aide mais j'espère que vous apprécierez le temps que j'ai pris pour écrire ceci :)



0
votes

Il n'est pas lié à async ou aux tâches. Le comportement que vous voyez provient du fait que EF doit générer et mettre en cache le modèle représenté par un contexte sur la première requête. Cela se produit avant le point ASYNC dans SAVECHANGES () puisque EF doit déterminer les requêtes à générer, d'où la pause.

Ce que vous pouvez faire pour aider les choses le long est d'utiliser une requête «Ne rien faire» lors de la démarrage de l'application, peut-être dans un autre fil, ce qui provoque cette mise en cache de se produire au lieu de l'action de l'utilisateur.


0 commentaires