Dans mon service, j'ai un thread d'arrière-plan qui fait un meilleur effort de sauvegarde d'un flux d'objet de certain type d'entité. Code à peu près est le suivant:DB ConnectionState = Ouvert mais context.SaveChanges génère une exception "connection broken"
while (AllowRun)
{
try
{
using (DbContext context = GetNewDbContext())
{
while (AllowRun && context.GetConnection().State == ConnectionState.Open)
{
TEntity entity = null;
try
{
while (pendingLogs.Count > 0)
{
lock (pendingLogs)
{
entity = null;
if (pendingLogs.Count > 0)
{
entity = pendingLogs[0];
pendingLogs.RemoveAt(0);
}
}
if (entity != null)
{
context.Entities.Add(entity);
}
}
context.SaveChanges();
}
catch (Exception e)
{
// (1)
// Log exception and continue execution
}
}
}
}
catch (Exception e)
{
// Log context initialization failure and continue execution
}
}
(c'est surtout le code actuel, j'omis quelques parties non pertinentes qui tentent de garder des objets éclatés en mémoire jusqu'à ce que nous sommes en mesure de sauver des choses à DB à nouveau lorsque exception est pris à Donc, essentiellement, il y a une boucle sans fin, essayant de lire des éléments de certaines listes et de les enregistrer dans Db. Si nous détectons que la connexion à la base de données a échoué pour une raison quelconque, elle tente simplement de la rouvrir et de continuer. Le problème est que parfois (je ne ai pas comprendre comment le reproduire jusqu'à présent), le code ci-dessus lorsque context.SaveChanges()
est appelé commence à produire exception suivante (pris dans (1)
bloc):
System.Data.EntityException: An error occurred while starting a transaction on the provider connection. See the inner exception for details. --->
System.InvalidOperationException: The requested operation cannot be completed because the connection has been broken.
L'erreur est enregistrée, mais lorsque l'exécution retourne à la vérification context.GetConnection().State == ConnectionState.Open
, elle est évaluée à true. Nous sommes donc dans un état où le contexte signale que sa connexion DB est ouverte, mais nous ne pouvons pas exécuter de requêtes dans ce contexte. Redémarrer le service supprime le problème (ainsi que de jouer avec la variable AllowRun
dans le débogueur pour forcer la recréation du contexte). Donc la question est puisque je ne peux pas croire l'état de connexion du contexte, comment puis-je vérifier que je peux exécuter des requêtes contre DB?
De même, y a-t-il un moyen propre de comprendre que la connexion n'est pas dans un état "sain"? Je veux dire, le EntityException
en soi n'est pas une indication que je devrais réinitialiser la connexion, seulement si son InnerException est InvalidOperationException
avec un message spécifique, alors oui, il est temps de le réinitialiser. Mais, maintenant je suppose qu'il y aurait d'autres situations où ConnectionState indique que tout va bien, mais je ne peux pas interroger DB. Puis-je les prendre de manière proactive, sans attendre que ça commence à me mordre?
Pouvez-vous fournir trace de la pile détaillée? Semble que soit une transaction quelque chose foiré connexion EF en cours ou un problème de délai de connexion expérimenté (éventuellement de MSDTC si les transactions distribuées étant applicables). –
La plupart des parties non pertinentes que vous avez omises sont vraiment pertinentes. Dans l'ensemble, il n'est pas nécessaire de maintenir la connexion pendant la durée de cette opération. La seule façon d'utiliser la connexion est l'appel SaveChanges (par la façon dont vous l'appelez à plusieurs reprises si votre exemple même si pendingLogs est vide). Créez simplement un nouveau contexte lorsque pendingLogs n'est pas vide (la connexion sera renvoyée du pool le plus souvent) et disposez après SaveChanges (la connexion sera renvoyée au pool). Vérifiez également si vos transactions augmentent à distribué et pourquoi (vous appelez peut-être plusieurs bases de données dans un transacton, etc.). – Evk
Pourquoi obtenez-vous un contexte avec une connexion ouverte en premier lieu? –