Il existe déjà un DataReader ouvert associé à cette commande qui doit être fermé en premier

641

J'ai cette requête et j'obtiens l'erreur dans cette fonction:

var accounts = from account in context.Accounts
               from guranteer in account.Gurantors
               select new AccountsReport
               {
                   CreditRegistryId = account.CreditRegistryId,
                   AccountNumber = account.AccountNo,
                   DateOpened = account.DateOpened,
               };

 return accounts.AsEnumerable()
                .Select((account, index) => new AccountsReport()
                    {
                        RecordNumber = FormattedRowNumber(account, index + 1),
                        CreditRegistryId = account.CreditRegistryId,
                        DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
                        AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
                    })
                .OrderBy(c=>c.FormattedRecordNumber)
                .ThenByDescending(c => c.StateChangeDate);


public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
{
    return (from h in context.AccountHistory
            where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
            select h.LastUpdated).Max();
}

L'erreur est:

Il existe déjà un DataReader ouvert associé à cette commande qui doit être fermé en premier.

Mise à jour:

trace de pile ajoutée:

InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.]
   System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command) +5008639
   System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command) +23
   System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async) +144
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, DbAsyncResult result) +87
   System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +32
   System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method) +141
   System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior behavior) +12
   System.Data.Common.DbCommand.ExecuteReader(CommandBehavior behavior) +10
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +443

[EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.]
   System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand, CommandBehavior behavior) +479
   System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute(ObjectContext context, ObjectParameterCollection parameterValues) +683
   System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) +119
   System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() +38
   System.Linq.Enumerable.Single(IEnumerable`1 source) +114
   System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3(IEnumerable`1 sequence) +4
   System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle(IEnumerable`1 query, Expression queryRoot) +29
   System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute(Expression expression) +91
   System.Data.Entity.Internal.Linq.DbQueryProvider.Execute(Expression expression) +69
   System.Linq.Queryable.Max(IQueryable`1 source) +216
   CreditRegistry.Repositories.CreditRegistryRepository.DateLastUpdated(Int64 creditorRegistryId, String accountNo) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1497
   CreditRegistry.Repositories.CreditRegistryRepository.<AccountDetails>b__88(AccountsReport account, Int32 index) in D:\Freelance Work\SuperExpert\CreditRegistry\CreditRegistry\Repositories\CreditRegistryRepository.cs:1250
   System.Linq.<SelectIterator>d__7`2.MoveNext() +198
   System.Linq.Buffer`1..ctor(IEnumerable`1 source) +217
   System.Linq.<GetEnumerator>d__0.MoveNext() +96
DotnetSparrow
la source

Réponses:

1288

Cela peut se produire si vous exécutez une requête tout en itérant sur les résultats d'une autre requête. Votre exemple ne montre pas clairement où cela se produit car l'exemple n'est pas complet.

Une chose qui peut provoquer cela est le chargement paresseux déclenché lors de l'itération sur les résultats d'une requête.

Cela peut être facilement résolu en autorisant MARS dans votre chaîne de connexion. Ajoutez MultipleActiveResultSets=trueà la partie fournisseur de votre chaîne de connexion (où la source de données, le catalogue initial, etc. sont spécifiés).

Ladislav Mrnka
la source
34
Cela a fonctionné pour moi. Si vous souhaitez en savoir plus sur l'activation de plusieurs jeux de résultats actifs (MARS), consultez msdn.microsoft.com/en-us/library/h32h3abf(v=vs.100).aspx . Pensez également à lire sur les inconvénients de MARS stackoverflow.com/questions/374444/…
Diganta Kumar
3
En tenant compte des performances, vous pouvez également résoudre ce problème en incluant System.Data.Entity, puis en utilisant les instructions Include pour vous assurer que ces données secondaires sont chargées dans la requête d'origine. Si vous activez MARS, le désactiver pour vérifier ces chargements de données répétés peut accélérer vos appels de traitement de données en réduisant les allers-retours.
Chris Moschini
70
L'activation de MARS ne doit être effectuée que pour un très petit sous-ensemble de problèmes / cas d'utilisation. Dans la plupart des cas, l'erreur en question est causée par un MAUVAIS CODE dans l'application appelante. Plus de détails ici: devproconnections.com/development/…
Michael K. Campbell
132
Ajouter .ToList () après your.Include (). Where () résoudra probablement le problème.
Serj Sagan
2
Faire un changement global de connexion SQL globale pour une requête est ridicule. La bonne réponse devrait être la liste ci-dessous. Un correctif local (c'est-à-dire qu'il suffit de modifier la requête) pour un problème localisé!
bytedev
218

Vous pouvez utiliser la ToList()méthode avant l' returninstruction.

var accounts =
from account in context.Accounts
from guranteer in account.Gurantors

 select new AccountsReport
{
    CreditRegistryId = account.CreditRegistryId,
    AccountNumber = account.AccountNo,
    DateOpened = account.DateOpened,
};

 return accounts.AsEnumerable()
               .Select((account, index) => new AccountsReport()
                       {
                           RecordNumber = FormattedRowNumber(account, index + 1),
                           CreditRegistryId = account.CreditRegistryId,
                              DateLastUpdated = DateLastUpdated(account.CreditRegistryId, account.AccountNumber),
                           AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)}).OrderBy(c=>c.FormattedRecordNumber).ThenByDescending(c => c.StateChangeDate).ToList();


 public DateTime DateLastUpdated(long creditorRegistryId, string accountNo)
    {
        var dateReported = (from h in context.AccountHistory
                            where h.CreditorRegistryId == creditorRegistryId && h.AccountNo == accountNo
                            select h.LastUpdated).Max();
        return dateReported;
    }
kazem
la source
9
J'ai eu cette erreur tant de fois maintenant ... et chaque fois que j'oublie! La réponse à la question est toujours d'utiliser ToList ().
Cheesus Toast
1
Y a-t-il des inconvénients à cela? Si vous avez 100 000 lignes, je doute que cela puisse être bon.
Martin Dawson
2
@MartinMazzaDawson, vous avez vraiment besoin de 100 000 enregistrements à la fois pour l'exécution des requêtes ?? je pense que l'utilisation de la pagination est une bonne idée pour cette situation
kazem
désolé de soulever un vieux sujet mais je suis tombé sur la même erreur lors du développement d'un RepositoryPattern et je l'ai résolu en ajoutant ".ToList () ou Single () ou Count ()" à chaque méthode du Repository. Alors qu'au début je retournais juste ".AsEnumerable ()". Maintenant, ma question est: le référentiel doit-il renvoyer le "ToList ()", ou est-ce quelque chose qui doit être demandé au consommateur final (c'est-à-dire: la logique service / métier)
alessalessio
Travaille pour moi. L'ajout de .ToList résout le problème de prise en charge décimale dans JetEntityFrameworkProvider. Total = storeDb.OF_Carts.Where(x => x.CartId == ShoppingCartId).ToList().Sum(t => t.Quantity * t.Item.UnitPrice);
hubert17
39

utilisez la syntaxe .ToList()pour convertir les objets lus à partir de db en liste pour éviter d'être relus. J'espère que cela fonctionnera pour cela. Merci.

Icemark Muturi
la source
22

Voici une chaîne de connexion qui fonctionne pour quelqu'un qui a besoin d'une référence.

  <connectionStrings>
    <add name="IdentityConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\IdentityDb.mdf;Integrated Security=True;MultipleActiveResultSets=true;" providerName="System.Data.SqlClient" />
  </connectionStrings>
Yang Zhang
la source
15
L'activation de MARS est une solution de contournement, PAS une solution au problème.
SandRock
5
Depuis la page Documentation MARS: "Les opérations MARS ne sont pas thread-safe." Cela signifie que si le problème provient de plusieurs threads accédant au contexte, MARS n'est (probablement) pas la solution.
marsop
20

Dans mon cas, l'utilisation a Include()résolu cette erreur et, en fonction de la situation, peut être beaucoup plus efficace que l'émission de plusieurs requêtes quand tout peut être interrogé à la fois avec une jointure.

IEnumerable<User> users = db.Users.Include("Projects.Tasks.Messages");

foreach (User user in users)
{
    Console.WriteLine(user.Name);
    foreach (Project project in user.Projects)
    {
        Console.WriteLine("\t"+project.Name);
        foreach (Task task in project.Tasks)
        {
            Console.WriteLine("\t\t" + task.Subject);
            foreach (Message message in task.Messages)
            {
                Console.WriteLine("\t\t\t" + message.Text);
            }
        }
    }
}
Despertar
la source
C'est la meilleure solution si votre application ne nécessite pas MARS.
Fred Wilson
7

Je ne sais pas s'il s'agit d'une réponse en double ou non. Si c'est le cas, je suis désolé. Je veux juste faire savoir aux nécessiteux comment j'ai résolu mon problème en utilisant ToList ().

Dans mon cas, j'ai eu la même exception pour la requête ci-dessous.

int id = adjustmentContext.InformationRequestOrderLinks.Where(item => item.OrderNumber == irOrderLinkVO.OrderNumber && item.InformationRequestId == irOrderLinkVO.InformationRequestId).Max(item => item.Id);

J'ai résolu comme ci-dessous

List<Entities.InformationRequestOrderLink> links = adjustmentContext.InformationRequestOrderLinks
.Where(item => item.OrderNumber == irOrderLinkVO.OrderNumber && item.InformationRequestId == irOrderLinkVO.InformationRequestId).ToList();

int id = 0;

if (links.Any())
{
  id = links.Max(x => x.Id);
 }
if (id == 0)
{
//do something here
}
Ziggler
la source
5

Il semble que vous appelez DateLastUpdated à partir d'une requête active en utilisant le même contexte EF et DateLastUpdate envoie une commande au magasin de données lui-même. Entity Framework ne prend en charge qu'une seule commande active par contexte à la fois.

Vous pouvez refaçonner vos deux requêtes ci-dessus en une seule comme ceci:

return accounts.AsEnumerable()
        .Select((account, index) => new AccountsReport()
        {
          RecordNumber = FormattedRowNumber(account, index + 1),
          CreditRegistryId = account.CreditRegistryId,
          DateLastUpdated = (
                                                from h in context.AccountHistory 
                                                where h.CreditorRegistryId == creditorRegistryId 
                              && h.AccountNo == accountNo 
                                                select h.LastUpdated).Max(),
          AccountNumber = FormattedAccountNumber(account.AccountType, account.AccountNumber)
        })
        .OrderBy(c=>c.FormattedRecordNumber)
        .ThenByDescending(c => c.StateChangeDate);

J'ai également remarqué que vous appelez des fonctions comme FormattedAccountNumber et FormattedRecordNumber dans les requêtes. À moins que ce ne soient des procs ou des fonctions stockés que vous avez importés de votre base de données dans le modèle de données d'entité et mappés correctement, ceux-ci lèveront également des exceptions car EF ne saura pas comment traduire ces fonctions en instructions qu'il peut envoyer au magasin de données.

Notez également que l'appel à AsEnumerable ne force pas l'exécution de la requête. Jusqu'à ce que l'exécution de la requête soit différée jusqu'à son énumération. Vous pouvez forcer l'énumération avec ToList ou ToArray si vous le souhaitez.

James Alexander
la source
Si vous le souhaitez, vous pouvez refactoriser la requête que vous effectuez pour obtenir la dateLastUpdated directement dans votre projection Select pour la requête de rapport de comptes et obtenir l'effet souhaité sans l'erreur.
James Alexander
Je reçois la même erreur après avoir mis le code de fonction dans la requête principale
DotnetSparrow
2

En plus de la réponse de Ladislav Mrnka :

Si vous publiez et remplacez le conteneur sous l' onglet Paramètres , vous pouvez définir MultipleActiveResultSet sur True. Vous pouvez trouver cette option en cliquant sur Avancé ... et ce sera sous le groupe Avancé .

Alexander Troshchenko
la source
2

Pour ceux qui trouvent cela via Google;
J'obtenais cette erreur car, comme suggéré par l'erreur, je n'ai pas réussi à fermer un SqlDataReader avant d'en créer un autre sur le même SqlCommand, en supposant à tort qu'il serait récupéré en quittant la méthode dans laquelle il a été créé.

J'ai résolu le problème en appelant sqlDataReader.Close();avant de créer le deuxième lecteur.

timelmer
la source
2

Dans mon cas, j'avais ouvert une requête à partir du contexte de données, comme

    Dim stores = DataContext.Stores _
        .Where(Function(d) filter.Contains(d.code)) _

... et ensuite demandé la même chose ...

    Dim stores = DataContext.Stores _
        .Where(Function(d) filter.Contains(d.code)).ToList

L'ajout .ToListde au premier a résolu mon problème. Je pense qu'il est logique d'envelopper cela dans une propriété comme:

Public ReadOnly Property Stores As List(Of Store)
    Get
        If _stores Is Nothing Then
            _stores = DataContext.Stores _
                .Where(Function(d) Filters.Contains(d.code)).ToList
        End If
        Return _stores
    End Get
End Property

Où _stores est une variable privée et Filters est également une propriété en lecture seule qui lit depuis AppSettings.

Adam Cox
la source
1

J'ai eu la même erreur lorsque j'ai essayé de mettre à jour certains enregistrements dans la boucle de lecture. J'ai essayé la réponse la plus votée MultipleActiveResultSets=trueet j'ai trouvé que c'était juste une solution de contournement pour obtenir la prochaine erreur 

La nouvelle transaction n'est pas autorisée car d'autres threads s'exécutent dans la session

La meilleure approche, qui fonctionnera pour d'énormes ResultSets, consiste à utiliser des blocs et à ouvrir un contexte séparé pour chaque bloc, comme décrit dans  SqlException from Entity Framework - Une nouvelle transaction n'est pas autorisée car d'autres threads s'exécutent dans la session

Michael Freidgeim
la source
1

J'ai résolu ce problème en modifiant l'attente _accountSessionDataModel.SaveChangesAsync (); à _accountSessionDataModel.SaveChanges (); dans ma classe Repository.

 public async Task<Session> CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        await _accountSessionDataModel.SaveChangesAsync();
     }

Changé en:

 public Session CreateSession()
    {
        var session = new Session();

        _accountSessionDataModel.Sessions.Add(session);
        _accountSessionDataModel.SaveChanges();
     }

Le problème était que j'ai mis à jour les sessions dans le frontend après avoir créé une session (dans le code), mais parce que SaveChangesAsync se produit de manière asynchrone, la récupération des sessions a provoqué cette erreur car apparemment, l'opération SaveChangesAsync n'était pas encore prête.

woutercx
la source
1

Eh bien pour moi, c'était mon propre bug. J'essayais de lancer une INSERTutilisation SqlCommand.executeReader()alors que j'aurais dû utiliser SqlCommand.ExecuteNonQuery(). Il a été ouvert et jamais fermé, provoquant l'erreur. Attention à cet oubli.

Andrew Taylor
la source
C'était le même problème de mon côté. J'avais besoin de SqlCommand.executeReader () car j'obtiens l'ID de lignes insérées. Donc: j'ai utilisé SqlDataReader.Close (); Sql Command.Dispose (); Merci @Andrew Taylor
Fuat
1

Ceci est extrait d'un scénario réel:

  • Le code fonctionne bien dans un environnement Stage avec MultipleActiveResultSets est défini dans la chaîne de connexion
  • Code publié dans l'environnement de production sans MultipleActiveResultSets = true
  • Tant de pages / appels fonctionnent alors qu'une seule échoue
  • En regardant de plus près l'appel, un appel inutile est effectué vers la base de données et doit être supprimé
  • Définissez MultipleActiveResultSets = true dans Production et publiez du code nettoyé, tout fonctionne bien et efficacement

En conclusion, sans oublier MultipleActiveResultSets, le code peut avoir fonctionné longtemps avant de découvrir un appel db redondant qui pourrait être très coûteux, et je suggère de ne pas dépendre entièrement de la définition de l'attribut MultipleActiveResultSets mais aussi de découvrir pourquoi le code en a besoin où il a échoué .

utileBee
la source
1

Ce problème se produit très probablement en raison de la fonctionnalité de «chargement paresseux» d'Entity Framework. Habituellement, sauf si explicitement requis lors de la récupération initiale, toutes les données jointes (tout ce qui est stocké dans d'autres tables de base de données) sont récupérées uniquement lorsque cela est nécessaire. Dans de nombreux cas, c'est une bonne chose, car cela empêche de récupérer des données inutiles et améliore ainsi les performances des requêtes (pas de jointures) et économise la bande passante.

Dans la situation décrite dans la question, la récupération initiale est effectuée, et pendant la phase de "sélection", des données de chargement paresseux manquantes sont demandées, des requêtes supplémentaires sont émises, puis EF se plaint de "open DataReader".

La solution de contournement proposée dans la réponse acceptée permettra l'exécution de ces requêtes, et en effet, toute la requête aboutira.

Cependant, si vous examinez les demandes envoyées à la base de données, vous remarquerez plusieurs demandes - une demande supplémentaire pour chaque donnée manquante (chargée paresseusement). Cela pourrait être un tueur de performances.

Une meilleure approche consiste à dire à EF de précharger toutes les données chargées paresseuses nécessaires lors de la requête initiale. Cela peut être fait en utilisant l'instruction "Include":

using System.Data.Entity;

query = query.Include(a => a.LazyLoadedProperty);

De cette façon, toutes les jointures nécessaires seront effectuées et toutes les données nécessaires seront renvoyées en une seule requête. Le problème décrit dans la question sera résolu.

Illidan
la source
Ceci est une réponse valide, car je suis passé de l'utilisation d'Include à l'utilisation de EntityEntry.Collection (). Load (), et ma solution est passée du travail au cassé. Malheureusement, inclure pour un générique ne peut pas "ThenInclude" un autre générique, donc j'essaie toujours de faire fonctionner EntityEntry.Collection (). Load ().
AndrewBenjamin
0

J'utilise le service Web dans mon outil, où ces services récupèrent la procédure stockée. alors qu'un plus grand nombre d'outils client récupère le service Web, ce problème se pose. J'ai corrigé en spécifiant l'attribut synchronisé pour ces fonctions récupère la procédure stockée. maintenant ça marche bien, l'erreur ne s'est jamais montrée dans mon outil.

 [MethodImpl(MethodImplOptions.Synchronized)]
 public static List<t> MyDBFunction(string parameter1)
  {
  }

Cet attribut permet de traiter une demande à la fois. donc cela résout le problème.

Pranesh Janarthanan
la source
0

En remarque ... cela peut également se produire en cas de problème avec le mappage de données (interne) à partir d'objets SQL.

Par exemple...

J'ai créé un SQL Scalar Functionqui a accidentellement renvoyé un VARCHAR... puis ... l'a utilisé pour générer une colonne dans un VIEW. Le VIEWétait correctement mappé dans le DbContext... donc Linq l' appelait très bien. Cependant, l' entité attendait DateTime? et la chaîne deVIEW retour .

Quel jet bizarrement ...

"Il existe déjà un DataReader ouvert associé à cette commande qui doit être fermé en premier"

C'était difficile à comprendre ... mais après avoir corrigé les paramètres de retour ... tout allait bien

Prisonnier ZERO
la source
0

Dans mon cas, je devais régler la MultipleActiveResultSetsà Truela chaîne de connexion.
Puis il est apparu une autre erreur (la vraie) à propos de ne pas pouvoir exécuter 2 commandes (SQL) en même temps sur le même contexte de données! (EF Core, Code d'abord)
Donc la solution pour moi était de chercher toute autre exécution de commande asynchrone et de la transformer en synchrone , car je n'avais qu'un seul DbContext pour les deux commandes.

J'espère que ça t'aide

Dr TJ
la source