Je reçois actuellement cette erreur:
System.Data.SqlClient.SqlException: la nouvelle transaction n'est pas autorisée car d'autres threads s'exécutent dans la session.
lors de l'exécution de ce code:
public class ProductManager : IProductManager
{
#region Declare Models
private RivWorks.Model.Negotiation.RIV_Entities _dbRiv = RivWorks.Model.Stores.RivEntities(AppSettings.RivWorkEntities_connString);
private RivWorks.Model.NegotiationAutos.RivFeedsEntities _dbFeed = RivWorks.Model.Stores.FeedEntities(AppSettings.FeedAutosEntities_connString);
#endregion
public IProduct GetProductById(Guid productId)
{
// Do a quick sync of the feeds...
SyncFeeds();
...
// get a product...
...
return product;
}
private void SyncFeeds()
{
bool found = false;
string feedSource = "AUTO";
switch (feedSource) // companyFeedDetail.FeedSourceTable.ToUpper())
{
case "AUTO":
var clientList = from a in _dbFeed.Client.Include("Auto") select a;
foreach (RivWorks.Model.NegotiationAutos.Client client in clientList)
{
var companyFeedDetailList = from a in _dbRiv.AutoNegotiationDetails where a.ClientID == client.ClientID select a;
foreach (RivWorks.Model.Negotiation.AutoNegotiationDetails companyFeedDetail in companyFeedDetailList)
{
if (companyFeedDetail.FeedSourceTable.ToUpper() == "AUTO")
{
var company = (from a in _dbRiv.Company.Include("Product") where a.CompanyId == companyFeedDetail.CompanyId select a).First();
foreach (RivWorks.Model.NegotiationAutos.Auto sourceProduct in client.Auto)
{
foreach (RivWorks.Model.Negotiation.Product targetProduct in company.Product)
{
if (targetProduct.alternateProductID == sourceProduct.AutoID)
{
found = true;
break;
}
}
if (!found)
{
var newProduct = new RivWorks.Model.Negotiation.Product();
newProduct.alternateProductID = sourceProduct.AutoID;
newProduct.isFromFeed = true;
newProduct.isDeleted = false;
newProduct.SKU = sourceProduct.StockNumber;
company.Product.Add(newProduct);
}
}
_dbRiv.SaveChanges(); // ### THIS BREAKS ### //
}
}
}
break;
}
}
}
Modèle # 1 - Ce modèle se trouve dans une base de données sur notre serveur de développement. Modèle # 1 http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/bdb2b000-6e60-4af0-a7a1-2bb6b05d8bc1/Model1.png
Modèle n ° 2 - Ce modèle se trouve dans une base de données sur notre serveur Prod et est mis à jour chaque jour par des flux automatiques. texte alternatif http://content.screencast.com/users/Keith.Barrows/folders/Jing/media/4260259f-bce6-43d5-9d2a-017bd9a980d4/Model2.png
Remarque - Les éléments entourés de rouge dans le modèle n ° 1 sont les champs que j'utilise pour "mapper" au modèle n ° 2. Veuillez ignorer les cercles rouges dans le modèle n ° 2: cela vient d'une autre question que j'ai eue et à laquelle il est maintenant répondu.
Remarque: J'ai encore besoin de mettre un chèque isDeleted afin que je puisse le supprimer en douceur de DB1 s'il est sorti de l'inventaire de notre client.
Tout ce que je veux faire, avec ce code particulier, c'est connecter une entreprise dans DB1 avec un client dans DB2, obtenir leur liste de produits dans DB2 et l'insérer dans DB1 si elle n'est pas déjà là. La première fois devrait être un tirage complet de l'inventaire. Chaque fois qu'il y est exécuté, rien ne devrait se produire à moins qu'un nouvel inventaire n'entre dans le flux pendant la nuit.
Donc, la grande question - comment résoudre l'erreur de transaction que je reçois? Dois-je supprimer et recréer mon contexte à chaque fois à travers les boucles (cela n'a pas de sens pour moi)?
la source
Réponses:
Après avoir beaucoup arraché mes cheveux, j'ai découvert que les
foreach
boucles étaient les coupables. Ce qui doit arriver, c'est d'appeler EF mais de le retourner dans unIList<T>
de ce type cible puis de boucler sur leIList<T>
.Exemple:
la source
SaveChanges
pendant que vous extrayez des résultats de la base de données. Par conséquent, une autre solution consiste simplement à enregistrer les modifications une fois la boucle terminée.Comme vous l'avez déjà identifié, vous ne pouvez pas enregistrer à partir d'un fichier
foreach
qui continue de puiser dans la base de données via un lecteur actif.Appeler
ToList()
ouToArray()
convient pour les petits ensembles de données, mais lorsque vous avez des milliers de lignes, vous consommerez une grande quantité de mémoire.Il est préférable de charger les lignes en morceaux.
Compte tenu des méthodes d'extension ci-dessus, vous pouvez écrire votre requête comme suit:
L'objet interrogable sur lequel vous appelez cette méthode doit être commandé. En effet, Entity Framework ne prend
IQueryable<T>.Skip(int)
en charge que les requêtes ordonnées, ce qui est logique lorsque vous considérez que plusieurs requêtes pour des plages différentes nécessitent que l'ordre soit stable. Si l'ordre n'est pas important pour vous, il vous suffit de trier par clé primaire, car il est probable qu'il ait un index clusterisé.Cette version interrogera la base de données par lots de 100. Notez que cela
SaveChanges()
est appelé pour chaque entité.Si vous souhaitez améliorer considérablement votre débit, vous devez appeler
SaveChanges()
moins fréquemment. Utilisez plutôt un code comme celui-ci:Cela se traduit par 100 fois moins d'appels de mise à jour de base de données. Bien sûr, chacun de ces appels prend plus de temps à se terminer, mais vous finissez toujours par aller de l'avant. Votre kilométrage peut varier, mais ce fut un monde plus rapide pour moi.
Et cela contourne l'exception que vous voyiez.
EDIT J'ai revu cette question après avoir exécuté SQL Profiler et mis à jour quelques éléments pour améliorer les performances. Pour toute personne intéressée, voici quelques exemples de SQL qui montrent ce qui est créé par la base de données.
La première boucle n'a pas besoin de sauter quoi que ce soit, c'est donc plus simple.
Les appels suivants doivent ignorer les blocs de résultats précédents, donc introduit l'utilisation de
row_number
:la source
Nous avons maintenant publié une réponse officielle au bogue ouvert sur Connect . Les solutions de contournement que nous recommandons sont les suivantes:
Cette erreur est due à Entity Framework créant une transaction implicite lors de l'appel SaveChanges (). La meilleure façon de contourner l'erreur consiste à utiliser un modèle différent (c'est-à-dire à ne pas enregistrer en cours de lecture) ou à déclarer explicitement une transaction. Voici trois solutions possibles:
la source
En effet, vous ne pouvez pas enregistrer les modifications dans une
foreach
boucle en C # en utilisant Entity Framework.context.SaveChanges()
La méthode agit comme une validation sur un système de base de données standard (RDMS).Effectuez simplement toutes les modifications (qu'Entity Framework mettra en cache), puis enregistrez-les toutes à la fois en appelant
SaveChanges()
après la boucle (en dehors de celle-ci), comme une commande de validation de base de données.Cela fonctionne si vous pouvez enregistrer toutes les modifications en même temps.
la source
Mettez juste
context.SaveChanges()
après la fin de votreforeach
(boucle).la source
Utilisez toujours votre sélection comme liste
Par exemple:
Parcourez ensuite la collection tout en enregistrant les modifications
la source
FYI: à partir d'un livre et de quelques lignes ajustées parce que son stil valide:
L'appel de la méthode SaveChanges () commence une transaction qui annule automatiquement toutes les modifications persistantes dans la base de données si une exception se produit avant la fin de l'itération; sinon la transaction est validée. Vous pourriez être tenté d'appliquer la méthode après chaque mise à jour ou suppression d'entité plutôt qu'après la fin de l'itération, en particulier lorsque vous mettez à jour ou supprimez un grand nombre d'entités.
Si vous essayez d'appeler SaveChanges () avant que toutes les données aient été traitées, vous encourez une exception «Nouvelle transaction n'est pas autorisée car il existe d'autres threads en cours d'exécution dans la session». L'exception se produit car SQL Server ne permet pas de démarrer une nouvelle transaction sur une connexion qui a un SqlDataReader ouvert, même avec plusieurs jeux d'enregistrements actifs (MARS) activés par la chaîne de connexion (la chaîne de connexion par défaut d'EF active MARS)
Parfois, il vaut mieux comprendre pourquoi les choses se passent ;-)
la source
Faire vos listes interrogables dans .ToList () et cela devrait fonctionner correctement.
la source
J'obtenais ce même problème mais dans une situation différente. J'avais une liste d'articles dans une zone de liste. L'utilisateur peut cliquer sur un élément et sélectionner supprimer, mais j'utilise un proc stocké pour supprimer l'élément, car la suppression de l'élément nécessite beaucoup de logique. Lorsque j'appelle le proc stocké, la suppression fonctionne correctement, mais tout appel ultérieur à SaveChanges entraînera l'erreur. Ma solution a été d'appeler le proc stocké en dehors d'EF et cela a bien fonctionné. Pour une raison quelconque, lorsque j'appelle le proc stocké en utilisant la manière EF de faire les choses, cela laisse quelque chose de ouvert.
la source
SELECT
déclaration dans une procédure stockée qui produisait un jeu de résultats vide et si ce jeu de résultats n'était pas lu,SaveChanges
levait cette exception.Voici 2 autres options qui vous permettent d'appeler SaveChanges () dans un pour chaque boucle.
La première option consiste à utiliser un DBContext pour générer vos objets de liste à parcourir, puis à créer un deuxième DBContext pour appeler SaveChanges (). Voici un exemple:
La deuxième option consiste à obtenir une liste des objets de base de données à partir du DBContext, mais à ne sélectionner que les identifiants. Et puis parcourez la liste des id (vraisemblablement un int) et obtenez l'objet correspondant à chaque int, et appelez SaveChanges () de cette façon. L'idée derrière cette méthode est de saisir une grande liste d'entiers, est beaucoup plus efficace que d'obtenir une grande liste d'objets db et d'appeler .ToList () sur l'objet entier. Voici un exemple de cette méthode:
la source
Si vous obtenez cette erreur en raison de foreach et que vous devez vraiment enregistrer une entité en premier dans la boucle et utiliser l'identité générée plus loin dans la boucle, comme c'était le cas dans mon cas, la solution la plus simple consiste à utiliser un autre DBContext pour insérer une entité qui renverra l'ID et utilisera cet identifiant dans un contexte externe
Par exemple
la source
Donc, dans le projet, j'avais exactement le même problème, le problème n'était pas dans le
foreach
ou.toList()
c'était en fait dans la configuration AutoFac que nous avons utilisée. Cela a créé des situations étranges où l'erreur ci-dessus a été générée, mais également un tas d'autres erreurs équivalentes.C'était notre solution: cela a changé:
À:
la source
Je sais que c'est une vieille question mais j'ai fait face à cette erreur aujourd'hui.
et j'ai trouvé que cette erreur peut être levée lorsqu'un déclencheur de table de base de données obtient une erreur.
pour votre information, vous pouvez également vérifier vos déclencheurs de tables lorsque vous obtenez cette erreur.
la source
J'avais besoin de lire un énorme ResultSet et de mettre à jour certains enregistrements dans le tableau. J'ai essayé d'utiliser des morceaux comme suggéré dans la réponse de Drew Noakes .
Malheureusement, après 50000 enregistrements, j'ai eu OutofMemoryException. La réponse Entity Framework Large Data Set, Out of Memory Exception explique, que
La recommandation est de recréer votre contexte pour chaque lot.
J'ai donc récupéré les valeurs minimale et maximale de la clé primaire - les tables ont des clés primaires sous forme d'entiers incrémentiels automatiques, puis j'ai récupéré de la base de données des morceaux d'enregistrements en ouvrant le contexte pour chaque morceau. Après traitement, le contexte de bloc se ferme et libère la mémoire. Il garantit que l'utilisation de la mémoire n'augmente pas.
Voici un extrait de mon code:
FromToRange est une structure simple avec des propriétés From et To.
la source
Nous avons commencé à voir cette erreur «La nouvelle transaction n'est pas autorisée car il y a d'autres threads en cours d'exécution dans la session» après la migration d'EF5 vers EF6.
Google nous a amenés ici, mais nous n'appelons pas
SaveChanges()
dans la boucle. Les erreurs ont été générées lors de l'exécution d'une procédure stockée à l'aide de ObjectContext.ExecuteFunction dans une lecture de boucle foreach à partir de la base de données.Tout appel à ObjectContext.ExecuteFunction encapsule la fonction dans une transaction. Le début d'une transaction alors qu'il existe déjà un lecteur ouvert provoque l'erreur.
Il est possible de désactiver l'habillage du SP dans une transaction en définissant l'option suivante.
L'
EnsureTransactionsForFunctionsAndCommands
option permet au SP de s'exécuter sans créer sa propre transaction et l'erreur n'est plus déclenchée.DbContextConfiguration.EnsureTransactionsForFunctionsAndCommands, propriété
la source
J'étais également confronté au même problème.
Voici la cause et la solution.
http://blogs.msdn.com/b/cbiyikoglu/archive/2006/11/21/mars-transactions-and-sql-error-3997-3988-or-3983.aspx
Assurez-vous qu'avant de lancer des commandes de manipulation de données telles que des insertions, des mises à jour, vous avez fermé tous les lecteurs SQL actifs précédents.
L'erreur la plus courante concerne les fonctions qui lisent les données de la base de données et renvoient des valeurs. Par exemple pour des fonctions comme isRecordExist.
Dans ce cas, nous revenons immédiatement de la fonction si nous avons trouvé l'enregistrement et oublions de fermer le lecteur.
la source
Le code ci-dessous fonctionne pour moi:
la source
Dans mon cas, le problème est apparu lorsque j'ai appelé la procédure stockée via EF, puis que SaveChanges a levé cette exception. Le problème était d'appeler la procédure, l'énumérateur n'était pas disposé. J'ai corrigé le code de la manière suivante:
la source
Je suis beaucoup en retard à la fête, mais aujourd'hui, j'ai fait face à la même erreur et la façon dont j'ai résolu était simple. Mon scénario était similaire à ce code donné, je faisais des transactions DB à l'intérieur de boucles imbriquées pour chaque.
Le problème est qu'une transaction à base de données unique prend un peu plus de temps que pour chaque boucle, donc une fois que la transaction précédente n'est pas terminée, la nouvelle traction lève une exception, la solution consiste donc à créer un nouvel objet dans la boucle pour chaque boucle. où vous effectuez une transaction db.
Pour les scénarios mentionnés ci-dessus, la solution sera la suivante:
la source
Je suis un peu en retard, mais j'ai aussi eu cette erreur. J'ai résolu le problème en vérifiant où les valeurs étaient mises à jour.
J'ai découvert que ma requête était erronée et qu'il y avait plus de 250+ modifications en attente. J'ai donc corrigé ma requête, et maintenant cela fonctionne correctement.
J'espère que cela aidera à résoudre les problèmes futurs.
la source