Entity Framework: il existe déjà un DataReader ouvert associé à cette commande

J’utilise Entity Framework et occasionnellement, je vais recevoir cette erreur.

EntityCommandExecutionException {"There is already an open DataReader associated with this Command which must be closed first."} at System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands... 

Même si je ne fais pas de gestion de connexion manuelle.

cette erreur se produit par intermittence.

code qui déclenche l’erreur (raccourci pour faciliter la lecture):

  if (critera.FromDate > x) { t= _tEntitites.T.Where(predicate).ToList(); } else { t= new List(_tEntitites.TA.Where(historicPredicate).ToList()); } 

en utilisant le modèle Dispose afin d’ouvrir une nouvelle connexion à chaque fois.

 using (_tEntitites = new TEntities(GetEntityConnection())) { if (critera.FromDate > x) { t= _tEntitites.T.Where(predicate).ToList(); } else { t= new List(_tEntitites.TA.Where(historicPredicate).ToList()); } } 

toujours problématique

pourquoi ne pas réutiliser une connexion si elle est déjà ouverte.

Il ne s’agit pas de fermer la connexion. EF gère correctement la connexion. Si je comprends bien ce problème, il existe plusieurs commandes de récupération de données exécutées sur une seule connexion (ou une seule commande avec plusieurs sélections) alors que le prochain DataReader est exécuté avant que le premier ait terminé la lecture. La seule façon d’éviter l’exception est d’autoriser plusieurs DataReaders nesteds = activer MultipleActiveResultSets. Lorsque cela se produit toujours, un autre scénario se produit lorsque vous parcourez le résultat de la requête (IQueryable) et que vous déclenchez un chargement différé de l’entité chargée dans l’itération.

Vous pouvez également utiliser MARS (MultipleActiveResultSets) pour écrire votre code afin de ne pas ouvrir plusieurs jeux de résultats.

Qu’est-ce que vous pouvez faire est de récupérer les données en mémoire, de cette façon, vous n’aurez pas le lecteur ouvert. Cela est souvent causé par une itération dans un jeu de résultats tout en essayant d’ouvrir un autre jeu de résultats.

Exemple de code:

 public class MyContext : DbContext { public DbSet Blogs { get; set; } public DbSet Posts { get; set; } } public class Blog { public int BlogID { get; set; } public virtual ICollection Posts { get; set; } } public class Post { public int PostID { get; set; } public virtual Blog Blog { get; set; } public ssortingng Text { get; set; } } 

Disons que vous effectuez une recherche dans votre firebase database contenant les éléments suivants:

 var context = new MyContext(); //here we have one resultset var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5); foreach (var blog in largeBlogs) //we use the result set here { //here we try to get another result set while we are still reading the above set. var postsWithImportantText = blog.Posts.Where(p=>p.Text.Contains("Important Text")); } 

Nous pouvons faire une solution simple en ajoutant .ToList () comme ceci:

 var largeBlogs = context.Blogs.Where(b => b.Posts.Count > 5).ToList(); 

Cela oblige entityframework à charger la liste en mémoire. Ainsi, lorsque nous l’itérons dans la boucle foreach, il n’utilise plus le lecteur de données pour ouvrir la liste, mais plutôt en mémoire.

Je me rends compte que cela ne sera peut-être pas souhaitable si vous souhaitez par exemple charger certaines propriétés. Ceci est principalement un exemple qui, espérons-le, explique comment / pourquoi vous pourriez avoir ce problème, afin que vous puissiez prendre des décisions en conséquence

Il y a une autre façon de surmonter ce problème. Que ce soit une meilleure façon dépend de votre situation.

Le problème est dû à un chargement paresseux, donc une façon d’éviter cela est de ne pas avoir de chargement paresseux, grâce à l’utilisation de Include:

 var results = myContext.Customers .Include(x => x.Orders) .Include(x => x.Addresses) .Include(x => x.PaymentMethods); 

Si vous utilisez les Include appropriés, vous pouvez éviter d’activer MARS. Mais si vous en manquez un, vous obtiendrez l’erreur, donc activer MARS est probablement le moyen le plus simple de le résoudre.

Vous obtenez cette erreur, lorsque la collection que vous essayez d’itérer est en quelque sorte un chargement différé (IQueriable).

 foreach (var user in _dbContext.Users) { } 

La conversion de la collection IQueriable en une autre collection énumérable résoudra ce problème. Exemple

 _dbContext.Users.ToList() 

Remarque: .ToList () crée un nouvel ensemble à chaque fois et peut entraîner des problèmes de performances si vous manipulez des données volumineuses.

J’ai résolu le problème facilement (pragmatique) en ajoutant l’option au constructeur. Ainsi, je ne l’utilise que lorsque cela est nécessaire.

 public class Something : DbContext { public Something(bool MultipleActiveResultSets = false) { this.Database .Connection .ConnectionSsortingng = Shared.ConnectionSsortingng /* your connection ssortingng */ + (MultipleActiveResultSets ? ";MultipleActiveResultSets=true;" : ""); } ... 

essayez dans votre chaîne de connexion pour définir “MultipleActiveResultSets = true” cela permet multitâche sur la firebase database. “Server = yourserver; AttachDbFilename = firebase database; ID utilisateur = sa; mot de passe = blah; MultipleActiveResultSets = true; App = EntityFramework” Cela fonctionne pour moi … que votre connexion soit dans app.config ou que vous la définissiez par programmation … utile

Au départ, j’avais décidé d’utiliser un champ statique dans ma classe d’API pour référencer une instance de l’object MyDataContext (où MyDataContext est un object de contexte EF5), mais c’est ce qui semblait créer le problème. J’ai ajouté du code comme suit à chacune de mes méthodes API et cela a résolu le problème.

 using(MyDBContext db = new MyDBContext()) { //Do some linq queries } 

Comme d’autres personnes l’ont déclaré, les objects EF Data Context ne sont PAS thread-safe. Donc, les placer dans l’object statique causera éventuellement l’erreur “lecteur de données” dans les bonnes conditions.

Mon hypothèse initiale était que la création d’une seule instance de l’object serait plus efficace et permettrait une meilleure gestion de la mémoire. D’après ce que j’ai compris dans cette recherche, ce n’est pas le cas. En fait, il semble plus efficace de traiter chaque appel de votre API comme un événement isolé et sécurisé. S’assurer que toutes les ressources sont correctement libérées, car l’object est hors de scope.

Cela a du sens surtout si vous amenez votre API à la prochaine progression naturelle qui serait de l’exposer en tant qu’API WebService ou REST.

Divulgation

  • OS: Windows Server 2012
  • .NET: installé 4.5, projet utilisant 4.0
  • Source de données: MySQL
  • Cadre d’application: MVC3
  • Authentification: formulaires

J’ai remarqué que cette erreur se produit lorsque j’envoie un IQueriable à la vue et que je l’utilise dans un double foreach, où le foreach interne doit également utiliser la connexion. Exemple simple (ViewBag.parents peut être IQueriable ou DbSet):

 foreach (var parent in ViewBag.parents) { foreach (var child in parent.childs) { } } 

La solution simple consiste à utiliser .ToList() sur la collection avant de l’utiliser. Notez également que MARS ne fonctionne pas avec MySQL.

Un bon compromis entre l’activation de MARS et la récupération complète de l’ensemble de résultats consiste à ne récupérer que les identifiants dans une requête initiale, puis à parcourir les ID matérialisant chaque entité.

Par exemple (en utilisant les exemples d’entités “Blog et Posts” comme dans cette réponse ):

 using (var context = new BlogContext()) { // Get the IDs of all the items to loop through. This is // materialized so that the data reader is closed by the // time we're looping through the list. var blogIds = context.Blogs.Select(blog => blog.Id).ToList(); // This query represents all our items in their full glory, // but, items are only materialized one at a time as we // loop through them. var blogs = blogIds.Select(id => context.Blogs.First(blog => blog.Id == id)); foreach (var blog in blogs) { this.DoSomethingWith(blog.Posts); context.SaveChanges(); } } 

Cela signifie que vous ne tirez que quelques milliers d’entiers en mémoire, par opposition à des milliers de graphes d’object entiers, ce qui devrait minimiser l’utilisation de la mémoire tout en vous permettant de travailler élément par élément sans activer MARS.

Un autre avantage intéressant, comme on peut le voir dans l’exemple, est que vous pouvez enregistrer les modifications lorsque vous parcourez chaque élément au lieu d’attendre la fin de la boucle (ou une autre solution de ce type), même avec MARS activé (voir ici et ici ).

J’ai trouvé que j’avais la même erreur, et cela s’est produit lorsque j’utilisais un Func au lieu d’une Expression> pour votre predicate .

Une fois que j’ai changé tous les Func's en Expression's l’exception a cessé d’être lancée.

Je crois que EntityFramwork fait des choses intelligentes avec Expression's qui ne fait tout simplement pas avec Func's

Si nous essayons de regrouper une partie de nos conditions dans une méthode Func <> ou une extension, nous aurons cette erreur, supposons que nous ayons un code comme celui-ci:

 public static Func IsCurrent() { return p => (p.ValidFrom == null || p.ValidFrom < = DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now); } Or public static IEnumerable IsCurrent(this IEnumerable prices) { .... } 

Cela lancera l’exception si nous essayons de l’utiliser dans un Where (), ce que nous devrions plutôt faire est de construire un prédicat comme ceci:

 public static Expression> IsCurrent() { return p => (p.ValidFrom == null || p.ValidFrom < = DateTime.Now) && (p.ValidTo == null || p.ValidTo >= DateTime.Now); } 

De plus amples informations peuvent être lues à l’ adresse suivante : http://www.albahari.com/nutshell/predicatebuilder.aspx

Ce problème peut être résolu simplement en convertissant les données en une liste

  var details = _webcontext.products.ToList(); if (details != null) { Parallel.ForEach(details, x => { Products obj = new Products(); obj.slno = x.slno; obj.ProductName = x.ProductName; obj.Price = Convert.ToInt32(x.Price); li.Add(obj); }); return li; } 

Dans ma situation, le problème est dû à un enregistrement d’dependency injection. J’injectais un service d’étendue par requête qui utilisait un dbcontext dans un service enregistré singleton. Par conséquent, dbcontext a été utilisé dans plusieurs requêtes et donc dans l’erreur.

J’ai résolu ce problème en utilisant la section de code suivante avant la deuxième requête:

  ...first query while (_dbContext.Connection.State != System.Data.ConnectionState.Closed) { System.Threading.Thread.Sleep(500); } ...second query 

vous pouvez changer l’heure de sumil en millisecondes

PD Utile lors de l’utilisation de threads