I've had an asp.net website running live on our intranet for a couple of weeks now. I just got an email from my application_error emailer method with an unhandled exception.
Here it is (I've cleaned up some of the paths to make it better displayed)
Exception : Object reference not set to an instance of an object. Stack Trace : at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value) at TimesheetDomain.DataMappers.StaffMemberData.ReadStaff(SqlDataReader reader) in TimesheetDomain\DataMappers\StaffMemberData.cs:line 362
at TimesheetDomain.DataMappers.StaffMemberData.GetStaffMember(String name) in TimesheetDomain\DataMappers\StaffMemberData.cs:line 401
at TimesheetDomain.ServiceLayer.TimesheetManager.GetUserFromName(String name) in TimesheetDomain\ServiceLayer\TimesheetManager.cs:line 199
at UserVerification.GetCurrentUser() in \App_Code\UserVerification.cs:line 29 at WebTimesheets.OnInit(EventArgs e) in \WebTimesheets\WebTimesheets.master.cs:line 159
at System.Web.UI.Control.InitRecursive(Control namingContainer) at System.Web.UI.Control.InitRecursive(Control namingContainer) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)
Basically it looks like it's erroring at my ReadStaff method which reads a data reader to build staff member objects. Here is the bit of 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;
}
(Line 362 is 3rd last line)
I'm using an identity map (just read fowlers book on patterns and thought it was a good idea - may have done it wrong, happy for comments) but that's not overly relevant as later on I use the newMember
object elsewhere so if I remove that block the NullReferenceException
will occur.
I am struggling to see how on earth newMember
is null in the 3rd last line there (which is the line that errored).
Resharper/VS doesn't give me a warning that it could be null
- because there's the 3 constructors which I choose from.
Can anyone suggest where I can look to try and fix this error? It's only happened once and that method has been called thousands of times since the site went live.
Thanks
[EDIT] As Requested, here's the IComparer for staff member
/// <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");
}
}
and it's used in the IComparable implementation
/// <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);
}
It is almost certainly a threading issue - see this question and its accepted answer.
Dictionary<>.Insert()
will throw a NullReferenceException
internally if the dictionary instance is modified from another thread during the insert operation.