Comment dois-je gérer DbContext Lifetime dans MVC Core?

De la documentation

Les contextes Entity Framework doivent être ajoutés au conteneur de services à l’aide de la Scoped vie Scoped . Ceci est pris en charge automatiquement si vous utilisez les méthodes d’assistance décrites ci-dessus. Les référentiels qui utiliseront Entity Framework doivent utiliser la même durée de vie.

J’ai toujours pensé que je devrais créer un nouveau Context pour chaque unité de travail à traiter. Cela me laisse penser que, si j’ai un ServiceA et un ServiceB , qui appliquent différentes actions sur le DbContext , ils devraient obtenir une instance différente de DbContext .

La documentation se lit comme suit:

  • Transient objects Transient sont toujours différents. une nouvelle instance est fournie à chaque contrôleur et à chaque service.

  • Scoped objects Scoped sont les mêmes dans une requête, mais différents d’une requête à l’autre

Pour revenir à ServiceA et ServiceB , cela me semble, Transient est plus approprié.

J’ai recherché que le contexte ne devrait être enregistré qu’une fois par HttpRequest , mais je ne comprends vraiment pas comment cela fonctionne.

Surtout si nous examinons un service:

 using (var transaction = dbContext.Database.BeginTransaction()) { //Create some entity var someEntity = new SomeEntity(); dbContext.SomeEntity.Add(someEntity); //Save in order to get the the id of the entity dbContext.SaveChanges(); //Create related entity var relatedEntity = new RelatedEntity { SomeEntityId = someEntity.Id }; dbContext.RelatedEntity.Add(relatedEntity) dbContext.SaveChanges(); transaction.Commit(); } 

Ici, nous devons enregistrer le contexte afin d’obtenir l’ID d’une entité liée à une autre que nous venons de créer.

En même temps, un autre service pourrait mettre à jour le même contexte. D’après ce que j’ai lu, DbContext n’est pas thread-safe.

Devrais-je utiliser Transient dans ce cas? Pourquoi la documentation suggère-t-elle que je devrais utiliser Scoped ?

Est-ce que je manque une partie importante du cadre?

Comme d’autres l’ont déjà expliqué, vous devez utiliser une dépendance étendue pour les contextes de firebase database pour vous assurer qu’elle sera correctement réutilisée. En ce qui concerne la concurrence, rappelez-vous que vous pouvez également interroger la firebase database de manière asynchrone, de sorte que vous n’aurez peut-être pas besoin de threads.

Si vous avez besoin de threads, c’est-à-dire de travailleurs d’arrière-plan, il est probable que ceux-ci auront une durée de vie différente de celle de la demande. En tant que tels, ces threads ne doivent pas utiliser les dépendances extraites de la scope de la demande. Lorsque la demande se termine et que son étendue de dépendance est en cours de fermeture, les dépendances jetables sont correctement éliminées. Pour les autres threads, cela signifierait que leurs dépendances pourraient finir par être disposées bien qu’elles en aient encore besoin: Mauvaise idée.

Au lieu de cela, vous devez explicitement ouvrir une nouvelle étendue de dépendance pour chaque thread que vous créez. Vous pouvez le faire en injectant IServiceScopeFactory et en créant une étendue à l’aide de CreateScope . L’object résultant contiendra alors un fournisseur de services à partir duquel vous pourrez récupérer vos dépendances. Comme il s’agit d’une étendue distincte, les dépendances étendues telles que les contextes de firebase database seront recréées pour la durée de vie de cette étendue.

Afin d’éviter d’entrer dans le modèle de localisateur de service, vous devez envisager de créer un service central que votre thread exécute, qui regroupe toutes les dépendances nécessaires. Le fil pourrait alors faire ceci:

 using (var scope = _scopeFactory.CreateScope()) { var service = scope.ServiceProvider.GetService(); service.Run(); } 

BackgroundThreadService et toutes ses dépendances peuvent alors suivre la méthode d’dependency injection courante pour recevoir des dépendances.

Je crois que vous ne seriez pas confronté à des problèmes de concurrence dans la plupart des cas lorsque vous utilisez une durée de vie étendue. Même dans l’exemple que vous avez posté, il n’y a pas de problèmes de simultanéité, car les services de la demande en cours seront appelés ultérieurement. Je ne peux même pas imaginer le cas où vous exécuterez 2 services ou plus en parallèle (c’est possible mais pas habituel) dans le contexte d’une requête HTTP (étendue).

À long terme, c’est juste un moyen de stocker vos données (pour être simple ici). Il suffit de regarder quelques gestionnaires de durée de vie dans des frameworks DI populaires, tous fonctionnent à peu près de la même manière – il s’agit d’un dictionnaire comme des objects qui implémentent un modèle à usage unique. Utilisation de Transient Je pense que votre méthode d’object get renverra toujours la valeur null. Par conséquent, DI créera une nouvelle instance à chaque demande. SingleInstance stockera les objects dans quelque chose comme un dictionnaire concurrent statique afin que conteneur crée une instance une seule fois, puis en reçoit une existante.

Scoped est généralement un object scope utilisé pour stocker les objects créés. Dans un pipeline aspnet, cela signifie généralement la même chose que par requête (car la scope peut être transmise par le pipeline)

Pour être bref – ne vous inquiétez pas, utilisez simplement Scoped, il est sûr et vous pouvez appeler à la demande.

J’ai essayé d’être très simple dans mon explication, vous pouvez toujours consulter le code source pour trouver le détail des correspondances dont vous avez besoin ici https://github.com/aspnet/DependencyInjection