我是如何在构造函数之后立即出现此NullReferenceException错误的?

How did I get this NullReferenceException error here right after the constructor?

提问人:RodH257 提问时间:8/24/2009 最后编辑:Thomas FlinkowRodH257 更新时间:3/9/2021 访问量:32843

问:

我已经有一个 asp.net 网站在我们的内部网上运行了几个星期。我刚刚从我的 application_error 电子邮件发送器方法收到一封电子邮件,其中包含未处理的异常。

这里是(我已经清理了一些路径以使其更好地显示)

例外:对象引用未设置为对象的实例。 堆栈跟踪:在 System.Collections.Generic.Dictionary'2.Insert (TKey 键,TValue 值,布尔值) 在 System.Collections.Generic.Dictionary'2.Add(TKey 键,TValue 值) 在 TimesheetDomain\DataMappers\StaffMemberData.cs:第 362 行中的 TimesheetDomain.DataMappers.StaffMemberData.ReadStaff(SqlDataReader 读取器)

在 TimesheetDomain.DataMappers.StaffMemberData.GetStaffMember(字符串) name) 在 时间表域\数据映射器\员工成员数据.cs:行 401

在 TimesheetDomain.ServiceLayer.TimesheetManager.GetUserFromName(字符串) name) 在 时间表域\服务层\时间表管理器.cs:行 199

在 UserVerification.GetCurrentUser() 在 \App_Code\UserVerification.cs:line 中 29 在 WebTimesheets.OnInit(EventArgs e) 在 \WebTimesheets\WebTimesheets.master.cs:行 159

在 System.Web.UI.Control.InitRecursive(控件 namingContainer) 在 System.Web.UI.Control.InitRecursive(控件 namingContainer) 在 System.Web.UI.Page.ProcessRequestMain(布尔值 includeStagesBeforeAsyncPoint, 布尔值 includeStagesAfterAsyncPoint)

基本上,它看起来像是我的 ReadStaff 方法出错,该方法读取数据读取器以构建工作人员对象。下面是一段代码:

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;
}

(362 号线是倒数第三行) 我正在使用身份映射(只是阅读了关于模式的福勒书,并认为这是一个好主意 - 可能做错了,很高兴发表评论),但这并不过分相关,因为稍后我在其他地方使用该对象,所以如果我删除该块,就会发生。newMemberNullReferenceException

我正在努力了解那里的倒数第三行(这是出错的行)中到底是如何为空的。newMember

Resharper/VS 没有给我一个警告,它可能 - 因为我有 3 个构造函数可供选择。null

谁能建议我可以在哪里尝试修复这个错误?它只发生过一次,自网站上线以来,该方法已被调用数千次。

谢谢

[编辑] 根据要求,这是工作人员的 IComparer

/// <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");
    }
}

它用于 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# asp.net NullReferenceException

评论

1赞 Matt Hamilton 8/24/2009
哪一行是 TimesheetDomain\DataMappers\StaffMemberData.cs:line 362?
0赞 Mehrdad Afshari 8/24/2009
这与实际问题无关,但您可以用 .如果密钥尚不存在,它将添加密钥。if (!_staffMembers.ContainsKey(staffID)) _staffMembers.Add(staffID, newMember); else _staffMembers[staffID] = newMember;_staffMembers[staffID] = newMember;
0赞 RodH257 8/24/2009
对不起(没有说得很清楚)362 行是我的代码块中的倒数第三行,它的 _staffMembers.Add(staffID, newMember);谢谢mehrdad,我会记住的,我以为我这样做是有原因的,但现在不记得了,所以我会给你的版本一个

答:

0赞 shahkalpesh 8/24/2009 #1

正如其他人所说,比较可能会导致问题。
是否有任何比较条件包含空值?具体来说,字符串属性?

即,如果 firstname 或 lastname 或 emailId 为 null,并且如果它用于比较,则在字典中进行比较时可能会失败。

编辑:主管和StaffMember以及AdminStaff类有什么关系?
在代码中,将这两个实例强制转换为 StaffMember。我的猜测是,如果 Supervisor 和 StaffMember 类相关,这可能是一个问题。

EDIT2:字典实例的 scrope 是什么?它是否在应用程序级别/会话级别共享?是否有可能多个线程尝试从中读取/写入?

评论

0赞 RodH257 8/24/2009
我本来以为 CompareTo 方法会出现在堆栈跟踪中吗?代码已添加到主帖子中 我明白你的意思,我将添加对姓氏的 null 检查,尽管它们不应该为 null,但它们是在构造函数中设置的,并从 sql 数据库返回,其中它们被设置为“NOT NULL”值
0赞 RodH257 8/24/2009
Supervisor 和 AdminOfficer 都是 StaffMember 的类型。继承树为: Staff Supervisor AdminOfficer 工作人员可以是普通的旧 StaffMember、主管或 AdminOfficer。除了正常属性外,主管还有一份受其监督的工作人员名单。它们是这样分开的,主要是为了验证目的,当你登录时,你的用户名与一个StaffMember相关联 - 如果它是主管,你可以做某些StaffMember不能做的事情,如果你是管理员,你可以做更多的事情
0赞 shahkalpesh 8/24/2009
这是字符串属性为 null 的问题吗?您是否添加了对 null 的检查?在那之后是否发生异常?
0赞 RodH257 8/24/2009
感谢您的输入 shahkalpesh,我已经添加了检查,这是一个无重现的错误(今天只收到 1 封电子邮件,方法已被调用数千次)。不过,我不相信 firstNames 可以为 null - 它们是直接从数据库中提取的,并且数据库会消除名字的 null 值。
1赞 rism 8/24/2009 #2

我看不出任何明显的东西。我会运行一些 SQL 来检查数据库中是否有任何不良数据。问题可能是相关输入表单中的怪异错误。如果到目前为止,代码已经运行了数千次而没有发生任何事件,我会在有问题的代码块周围包装一些额外的异常处理/报告,这样您至少可以在下次发生时获得 staffId。

你可以在这样的事情上花费很多时间。最方便的方法可能只是让它在上述/受控条件下再次失败......假设它造成的中断程度是可接受的/可管理的/轻微的。

我很欣赏这不会满足立即知道的需求,但它可能是解决问题的最佳方法,尤其是在如此低的失败率下。

评论

0赞 RodH257 8/24/2009
是的,这可能是最好的主意,我会稍微收紧一下,并尝试将其设置为更好的错误消息,看看它是否会再次发生。并没有太大影响,我猜他们只需要刷新页面。
1赞 Akash Kava 8/24/2009 #3

它只发生过一次,而且 方法已被调用数千 自网站上线以来的次数。

读完这篇文章后,我可以得出结论,.NET 可能已经耗尽了它的内存,它无法创建更多的字典键,这可能真的不是你的错。但是,是的,当我们尝试在会话/应用程序变量中存储过多信息时,我们确实遇到了此类错误,从而增加了 Web 应用程序的内存占用。但是当我们的数字非常高时,我们会遇到这样的错误,比如在字典或列表中存储 10,000 个项目等。

模式很好,但你也必须意识到,我们使用数据库以关系格式存储信息,如果我们开始使用内存来存储类似的东西,那么我们就忽略了强大的数据库。数据库也可以为您缓存值。

这听起来可能很傻,但我们每 24 小时重新启动一次 Windows 服务器,在没有流量的午夜。这确实帮助我们摆脱了这些错误。我们定期按修复计划重新启动服务器,以清除所有缓存/日志。

142赞 McKenzieG1 2/4/2010 #4

这几乎可以肯定是一个线程问题 - 请参阅此问题及其公认的答案

Dictionary<>.Insert()如果在插入操作期间从另一个线程修改了字典实例,则会在内部抛出一个。NullReferenceException

评论

4赞 Piedone 3/25/2015
这完全不是显而易见的,特别是如果你只从外面看到一个 IDictionary......
0赞 Todd 8/18/2012 #5

我看到这种异常的另一种方式,与线程无关,是在序列化字典时。在本例中,它是空的,但我仍然在反序列化实例的 Insert() 方法中获得了 NullReferenceException。

就我而言,简单的更改只是在反序列化后创建一个新实例。我不确定序列化是否只是破坏了字典,或者它是否是定义字典的类型。

就我而言,如果没有序列化代理项,这些类型是不可序列化的(我已经提供了这些),但也许字典中的某些内容在这里有问题。然而,字典又是空的,这仍然发生了。

22赞 Bill Noto 1/18/2013 #6

从 .NET 4.0 开始,可以使用 ConcurrentDictionary 并避免与同时从多个线程操作同一字典相关的线程问题。

评论

0赞 Felix K. 7/14/2022
不要说得太大声,因为 ConcurrentDictionary 也有它的陷阱,例如,如果您创建资源对象,则可能会创建重复的对象并发生内存泄漏。