Un site Web asp.net est en ligne sur notre intranet depuis quelques semaines maintenant. Je viens de recevoir un courrier électronique de ma méthode email_application_error avec une exception non gérée.
Le voici (j'ai nettoyé quelques chemins pour le rendre mieux affiché)
Exception: la référence d'objet n'est pas définie sur une instance d'objet. Trace de pile: à System.Collections.Generic.Dictionary`2.Insert (clé TKey, valeur TValue, addition booléenne) à System.Collections.Generic.Dictionary`2.Add (clé TKey, valeur TValue) à TimesheetDomain.DataMappers.StaffMemberData .ReadStaff (lecteur SqlDataReader) dans TimesheetDomain\DataMappers\StaffMemberData.cs: ligne 362
at TimesheetDomain.DataMappers.StaffMemberData.GetStaffMember (Nom de la chaîne) dans TimesheetDomain\DataMappers\StaffMemberData.cs: ligne 401
at TimesheetDomain.ServiceLayer.TimesheetManager.GetUserFromName (nom de chaîne) dans TimesheetDomain\ServiceLayer\TimesheetManager.cs: line 199
sur UserVerification.GetCurrentUser () dans\App_Code\UserVerification.cs: ligne 29 sur WebTimesheets.OnInit (EventArgs e) dans\WebTimesheets\WebTimesheets.master.cs: ligne 159
sur System.Web.UI.Control.InitRecursive (Control namingContainer) sur System.Web.UI.Control.InitRecursive (Control namingContainer) sur System.Web.UI.Page.ProcessRequestMain (Boolean includeStagesBeforeCentenantAssayer, Aimer)
Fondamentalement, il semble que ce soit une erreur de ma méthode ReadStaff, qui lit un lecteur de données pour créer des objets membres du personnel. Voici le morceau de code:
while (reader != null && reader.Read())
{
StaffMember newMember = null;
string firstName = reader["FirstName"].ToString();
string lastName = reader["LastName"].ToString();
int staffID = (int)reader["StaffID"];
int employSection = (int)reader["EmploySection"];
StaffType employType = (StaffType)employSection;
string emailAddress = reader["EmailInt"].ToString();
int employCode = (int)reader["ibbwid"];
//check if they are an admin staff member
if (IsAdminStaff(employType))
{
newMember = new AdminOfficer(firstName, lastName, employType, staffID, emailAddress, employCode);
}
else
{
//check if they are a supervisor
if (IsASupervisor(staffID))
newMember = new Supervisor(firstName, lastName, employType, staffID, emailAddress, employCode);
else
newMember = new StaffMember(firstName, lastName, employType, staffID, emailAddress, employCode);
}
//add to identity map
if (!_staffMembers.ContainsKey(staffID))
_staffMembers.Add(staffID, newMember); //****THIS IS LINE 362*****
else
_staffMembers[staffID] = newMember;
}
(La ligne 362 est la 3ème dernière ligne) J'utilise une carte d'identité (il suffit de lire le livre de Fowlers sur les motifs et je pensais que c'était une bonne idée - je me suis peut-être trompé, content de pouvoir commenter), mais ce n'est pas très pertinent, car je l'utilise plus tard. l'objet newMember
ailleurs, donc si je supprime ce bloc, le NullReferenceException
se produira.
J'ai du mal à comprendre comment newMember
est nul dans la 3ème dernière ligne (qui est la ligne erronée).
Resharper/VS ne me prévient pas que cela pourrait être null
- car il y a les 3 constructeurs parmi lesquels je choisis.
Quelqu'un peut-il suggérer où je peux chercher pour essayer de corriger cette erreur? Cela n'est arrivé qu'une fois et cette méthode a été appelée des milliers de fois depuis la mise en ligne du site.
Merci
[EDIT] Comme demandé, voici l'IComparer pour le membre du personnel
/// <summary>
/// Comparer for staff members - compares on name
/// </summary>
public class StaffMemberComparer : IComparer
{
public int Compare(object x, object y)
{
//check they are staff members
if (x is StaffMember && y is StaffMember)
{
//do a simple string comparison on names
StaffMember staffX = x as StaffMember;
StaffMember staffY = y as StaffMember;
return String.Compare(staffX.FirstName, staffY.FirstName);
}
throw new Exception("This is for comparing Staff Members");
}
}
et il est utilisé dans l'implémentation IComparable
/// <summary>
/// IComparable implementaiton
/// </summary>
/// <param name="obj">object to compare to</param>
/// <returns></returns>
public int CompareTo(object obj)
{
StaffMemberComparer comparer = new StaffMemberComparer();
return comparer.Compare(this, obj);
}
C’est presque certainement une question d’activités - voir cette question et sa réponse acceptée .
Dictionary<>.Insert()
lancera une NullReferenceException
en interne si l'instance du dictionnaire est modifiée à partir d'un autre thread au cours de l'opération d'insertion.
À partir de .NET 4.0, vous pouvez utiliser ConcurrentDictionary et éviter les problèmes de thread liés à la manipulation simultanée du même dictionnaire à partir de plusieurs threads.
Je ne vois rien d’évident. Je courrais un peu de SQL pour vérifier la base de données pour toutes les données incorrectes. Le problème peut être un bug anormal dans un formulaire de saisie associé. Si le code a été exécuté des milliers de fois sans incident jusqu'à présent, je voudrais encapsuler du traitement/des exceptions supplémentaires autour du bloc de code en question afin que vous puissiez au moins obtenir un staffId si/quand cela se produira ensuite.
Vous pourriez perdre beaucoup de temps sur quelque chose comme ça. La solution la plus appropriée consiste peut-être simplement à laisser l’échec se produire à nouveau dans les conditions susmentionnées/contrôlées… en supposant que le niveau de perturbation qu’il provoque soit acceptable/gérable/mineur.
Je comprends que cela ne satisfera pas le besoin immédiat de savoir, mais que c'est peut-être le meilleur moyen de gérer le problème, en particulier avec un taux d'échec aussi bas.
Cela n'est arrivé qu'une fois et que la méthode A été appelée des milliers de Fois depuis la mise en ligne du site.
Après avoir lu ceci, je peux en conclure que c’est possible que .NET ait épuisé sa mémoire et qu’il ne puisse plus créer de clé de dictionnaire, ce n’est peut-être pas votre faute. Mais oui, nous avons eu ce genre d’erreurs lorsque nous avons essayé de stocker trop d’informations dans des variables de session/d’application, augmentant ainsi l’empreinte mémoire de l’application Web. Mais nous avons eu de telles erreurs lorsque nos chiffres sont devenus très élevés, comme stocker 10 000 éléments dans Dictionnaire ou Liste, etc.
Le modèle est bon, mais vous devez également vous rendre compte que nous utilisons une base de données pour stocker des informations au format relationnel. Si nous commençons à utiliser la mémoire pour stocker des choses similaires, nous ignorons une base de données puissante. La base de données peut également mettre en cache des valeurs pour vous.
Cela peut paraître idiot, mais notre serveur Windows doit redémarrer toutes les 24 heures, à minuit, lorsqu'il n'y a pas de trafic. Cela nous a aidés à nous débarrasser de telles erreurs. Nous redémarrons nos serveurs régulièrement selon un horaire fixe afin de vider tous les caches/journaux.
Une autre façon de voir cette exception, sans rapport avec le threading, consiste à sérialiser un dictionnaire. Dans ce cas, il était vide, mais j'ai quand même eu l'exception NullReferenceException dans la méthode Insert () sur l'instance désérialisée.
Le changement simple dans mon cas consistait simplement à créer une nouvelle instance après la désérialisation. Je ne sais pas si la sérialisation rompt simplement le dictionnaire ou si ce sont les types pour lesquels le dictionnaire est défini.
Dans mon cas, les types n'étaient pas sérialisables sans substituts de sérialisation (et je les avais fournis), mais peut-être que quelque chose dans le dictionnaire avait un problème ici. Encore une fois cependant, le dictionnaire était vide et cela se produisait toujours.
Comme d'autres l'ont dit, la comparaison pourrait être à l'origine du problème.
L'un des critères de comparaison contient-il une valeur nulle? spécifiquement, les propriétés de la chaîne?
i.e. Si le prénom ou le nom de famille ou l’adresse emailId sont nuls et qu’ils sont utilisés à des fins de comparaison, les choses pourraient échouer lorsqu’elles sont utilisées à l’intérieur du dictionnaire.
EDIT: Quel est le lien entre les classes Supervisor and StaffMember et AdminStaff?
Dans le code, vous transmettez les deux instances à StaffMember. Je suppose que cela pourrait poser un problème si les classes Supervisor et StaffMember sont non liées.
EDIT2: Quel est le scrope de l'instance du dictionnaire? Est-ce partagé au niveau de l'application/de la session? Est-il possible que plusieurs threads essaient de lire/écrire à partir de celui-ci?