Comment attraper l'original (intérieure) exception en C#?
je suis d'appeler une fonction qui renvoie une exception personnalisée:
GetLockOwnerInfo(...)
Cette fonction à son tour, est l'appel d'une fonction qui renvoie une exception:
GetLockOwnerInfo(...)
ExecuteReader(...)
Cette fonction à son tour, est l'appel d'une fonction qui renvoie une exception:
GetLockOwnerInfo(...)
ExecuteReader(...)
ExecuteReader(...)
Et ainsi de suite:
GetLockOwnerInfo(...)
ExecuteReader(...)
ExecuteReader(...)
ExecuteReaderClient(...)
Fill(...)
L'une de ces fonctions renvoie une SqlException
, bien que ce code n'a aucune idée de ce qu'est un SqlException
est.
Des niveaux plus élevés d'envelopper ce SqlException
dans un autre BusinessRuleException
afin d'inclure certaines propriétés spéciales et des détails supplémentaires, tout en incluant le "original" exception comme InnerException
:
catch (DbException ex)
{
BusinessRuleExcpetion e = new BusinessRuleException(ex)
...
throw e;
}
Des niveaux plus élevés d'envelopper ce BusinessRuleException
dans un autre LockerException
afin d'inclure certaines propriétés spéciales et des détails supplémentaires, tout en incluant le "original" exception comme InnerException
:
catch (BusinessRuleException ex)
{
LockerException e = new LockerException(ex)
...
throw e;
}
Le problème maintenant est que je veux attraper le origianl SqlException
, pour vérifier un code d'erreur particulier.
Mais il n'y a aucun moyen de "rattraper l'exception interne":
try
{
DoSomething();
}
catch (SqlException e)
{
if (e.Number = 247)
{
return "Someone";
}
else
throw;
}
je pensais au sujet de la capture SqlException
droit quand il est lancé, et la copie de valeurs différentes pour la re-levée d'une exception - mais ce code ne dépend pas de Sql. Elle connaît un SqlException
, mais il n'a pas de dépendance sur SqlException.
j'ai pensé à attraper toutes les exceptions:
try
{
DoSomething(...);
}
catch (Exception e)
{
SqlException ex = HuntAroundForAnSqlException(e);
if (ex != null)
{
if (e.Number = 247)
{
return "Someone";
}
else
throw;
}
else
throw;
}
Mais c'est horrible de code.
Étant donné que .NET ne vous permettent pas de modifier la Message
d'un Exception
pour inclure des informations supplémentaires, quel est le mécanisme prévu pour attraper origine des exceptions?
- Si votre code à l'extérieur ne se soucient pas de savoir SqlException, il ne devrait probablement pas être intéressés par l'accès à ce champs. Je dirais que la couche enveloppant dans une nouvelle exception devrait se traduire SQL spécificités qui peuvent être d'intérêt pour l'équivalent générique (comme un FailureReason enum ou quelque chose).
- Le problème est que la personne qui attrape le
DbException
ne pas/ne peut pas avoir une dépendance surSystem.Data.SqlClient
Vous devez vous connecter pour publier un commentaire.
J'ai hate d'avoir à vous dire cela, mais vous ne pouvez pas attraper un intérieur d'exception.
Ce que vous pouvez faire est de inspecter un.
Je vous suggère de prendre votre haut niveau d'exception (je crois que c'était
LockerException
) et d'inspecter leInnerException
des biens de cette exception. Vérifier le type, et si ce n'est pas unSqlException
, vérifier laInnerException
de cette exception. À pied chaque une jusqu'à trouver unSqlException
type, puis d'obtenir les données dont vous avez besoin.Cela dit, je suis d'accord avec dasblinkenlight que vous devriez envisager, si possible, un lourd refactoriser de votre cadre d'exception.
System.Data.SqlClient
, pas plus que je pouvais aussi leur faire prendre une dépendance surIBM.Data.DB2
Vous avez besoin de c# 6 /visual studio 2015, pour ce faire, à l'aide d'un prédicat:
Officiel C# Try/Catch De La Documentation
Vérifier le code d'erreur d'un enveloppé exception n'est pas une bonne pratique, parce que ça fait mal d'encapsulation plutôt sévèrement. Imaginez à un certain point, la réécriture de la logique à lire à partir d'un non-SQL source, par exemple, un service web. Il serait de jeter quelque chose autre que
SQLException
sous la même condition, et à l'extérieur de code n'aurait aucun moyen de le détecter.Vous devez ajouter le code pour le bloc d'attraper
SQLException
pour vérifiere.Number = 247
à droite, puis et là, et de le jeterBusinessRuleException
avec une propriété qui le différencie deBusinessRuleException
jeté en réponse à la non-SQLException
etSQLException
avece.Number != 247
de manière significative. Par exemple, si le nombre magique247
signifie que vous avez rencontré un duplicata (une pure spéculation de ma part en ce moment), vous pourriez faire quelque chose comme ceci:Lorsque vous attrapez
BusinessRuleException
plus tard, vous pouvez vérifier saDuplicateDetected
de la propriété, et d'agir en conséquence.EDIT 1 (en réponse à l'observation que le DB-lecture de code ne peut pas vérifier pour
SQLException
)Vous pouvez également modifier votre
BusinessRuleException
pour vérifierSQLException
dans son constructeur, comme ceci:C'est moins souhaitable, parce qu'il rompt l'encapsulation, mais au moins il le fait en un seul endroit. Si vous avez besoin d'examiner d'autres types d'exceptions (par exemple, parce que vous avez ajouté un service web source), vous pouvez l'ajouter à la
SetDuplicateDetectedFlag
méthode, et tout fonctionne de nouveau.DbExcpetion
n'est pas (et ne peut pas le savoir) à propos d'unSqlException
. EtDbException
est abtract - ne peut pas être levée.DbException
, il doit wrap dansBusinessRuleException
comme il le fait maintenant. La seule chose que le code doit faire différemment, c'est l'ajout d'une vérification pour examinerDbException
, voir si il estSQLException
avece.Number == 247
, et définir certaines propriétés supplémentaires deBusinessRuleException
pour passer ce peu d'info jusqu'à l'invocation de la chaîne.SqlException
, tant qu'il a un état d'erreur247
. Pas d'autres erreurs que je peux gérer. Si la mise en œuvre des modifications à un web-service, mon code certainement pas gérer.BusinessRuleException
ne peut pas gérerSqlException
. Il ne peut pas attraper un animal, il n'a aucune idée de ce qu'un tel animal est. Les gens plus élevé dans la trace de la pile peut (et il s'avère qu'ils n') savent ce qu'est unSqlException
est.Ayant une face externe de la couche application de soins sur les détails d'un enveloppé exception est une odeur de code; le plus profond de l'emballage, plus l'odeur. La classe qui vous avez maintenant enrouler le
SqlException
dans undbException
est sans doute conçu pour exposer une SqlClient en tant que générique de la base de données de l'interface. En tant que telle, cette classe devrait comporter un moyen de distinguer les différentes conditions exceptionnelles. Il peut, par exemple, définir un dbTimeoutWaitingForLockException et décider de le jeter quand il attrape une SqlException et détermine sur la base de son code d'erreur qu'il y a un verrouillage de délai d'attente. Dans vb.net il pourrait être plus propre d'avoir une dbException type qui expose une ErrorCause énumération, on pourrait alors direCatch Ex as dbException When ex.Cause = dbErrorCauses.LockTimeout
, mais malheureusement filtres d'exception ne sont pas utilisables en C#.Si l'on a une situation où à l'intérieur de la classe wrapper de ne pas en savoir assez sur ce qu'il fait de savoir comment elle devrait définir des exceptions, il peut être utile d'avoir à l'intérieur de la méthode de la classe d'accepter une exception de l'emballage-délégué qui prendrait une exception à l'intérieur de la classe a pris ou aurait "j'aime" à jeter, et l'envelopper dans une manière appropriée à l'extérieur de la classe. Une telle approche serait probablement exagéré dans les cas où l'intérieur de la classe est appelée directement à partir de l'extérieur de la classe, mais peut être utile si il y a des intermédiaires classes concernées.
SqlException
, mais unDbException
. Le code est fournisseur de données agnostique. je peux pas l'obliger à comprendreSqlException
,OleDbException
,OdbcException
,DB2Exception
- c'est pourquoi il garde l'exception dans leinner
.Bonne question et les bonnes réponses!
Je veux juste compléter les réponses déjà donné avec quelques réflexions:
D'une part je suis d'accord avec dasblinkenlight et les autres utilisateurs. Si vous attrapez une exception à renvoyer une exception de type différent avec l'original de l'exception définie comme l'intérieur d'exception, alors vous devez faire cela pour aucune autre raison que de maintenir la méthode du contrat. (L'accès à SQL server est un détail d'implémentation que l'appelant n'est pas/ne doit pas/ne peut pas en être conscient, il ne peut donc pas prévoir qu'un
SqlException
(ouDbException
d'ailleurs) sera lancé.)L'application de cette technique a cependant des implications que l'on doit être conscient de:
DbException
ont été autorisés à bulles haut de la pile des appels plus loin).StackTrace
propriété de l'exception interceptée sera point à un catch-bloc loin de l'emplacement de l'erreur à l'origine s'est produite. Cela peut faciliter le débogage notoirement difficile, sauf si vous prenezle plus grand soin pour vous connecter la pile des traces de toutes les exceptions internes ainsi. (Ceci est particulièrement vrai une fois que le logiciel a été déployé en production et vous n'avez aucun moyen de joindre un
débogueur...)
Il est vrai que .NET ne vous permettent pas de modifier le Message de l'Exception. Il constitue un autre mécanisme toutefois de fournir des informations supplémentaires à une Exception via le
Exception.Data
dictionnaire. Donc, si tout ce que vous voulez faire est d'ajouter des données supplémentaires d'une exception, il n'y a aucune raison pour envelopper l'exception d'origine et de lancer une nouvelle. Au lieu de faire juste: