【问题标题】:NullReferenceException makes me want to shoot myselfNullReferenceException 让我想自己开枪
【发布时间】:2025-11-22 15:40:02
【问题描述】:

好的,从前,我的代码有效。从那以后我做了一些重构(我认为是基本的东西),但现在我得到了一个空引用异常,我无法弄清楚为什么。

从这里开始。 注意,这是注销方法,但我所有的 ActivityLogs 部分都抛出此错误

Function LogOut(ByVal go As String) As ActionResult
    ActivityLogService.AddActivity(AuthenticationHelper.RetrieveAuthUser.ID, _
                                   IActivityLogService.LogType.UserLogout, _
                                   HttpContext.Request.UserHostAddress)
    ActivityLogService.SubmitChanges()
    'more stuff happens after this'
End Function

服务非常简单(请注意此处抛出的错误

    Public Sub AddActivity(ByVal userid As Integer, ByVal activity As Integer, ByVal ip As String) Implements IActivityLogService.AddActivity
        Dim _activity As ActivityLog = New ActivityLog With {.Activity = activity,
                                                             .UserID = userid,
                                                             .UserIP = ip.IPAddressToNumber,
                                                             .ActivityDate = DateTime.UtcNow}

        ActivityLogRepository.Create(_activity)  ''#ERROR THROWN HERE
    End Sub

Service使用的接口是这样的

Public Interface IActivityLogService
    Sub AddActivity(ByVal userid As Integer, ByVal activity As Integer, ByVal ip As String)
    Function GetUsersLastActivity(ByVal UserID As Integer) As ActivityLog
    Sub SubmitChanges()

    ''' <summary> '
    ''' The type of activity done by the user '
    ''' </summary>
    ''' <remarks>Each int refers to an activity. There can be no duplicates or modifications  after this application goes live</remarks> '
    Enum LogType As Integer
        ''' <summary> '
        ''' A new session started '
        ''' </summary> '
        SessionStarted = 1
        ''' <summary> '
        ''' A new user is Added/Created '
        ''' </summary> '
        UserAdded = 2
        ''' <summary> '
        ''' User has updated their profile '
        ''' </summary> '
        UserUpdated = 3
        ''' <summary> '
        ''' User has logged into they system '
        ''' </summary> '
        UserLogin = 4
        ''' <summary> '
        ''' User has logged out of the system '
        ''' </summary> '
        UserLogout = 5
        ''' <summary> '
        ''' A new event has been added '
        ''' </summary> '
        EventAdded = 6
        ''' <summary> '
        ''' An event has been updated '
        ''' </summary> '
        EventUpdated = 7
        ''' <summary> '
        ''' An event has been deleted '
        ''' </summary> '
        EventDeleted = 8
        ''' <summary> '
        ''' An event has received a Up/Down vote '
        ''' </summary> '
        EventVoted = 9
        ''' <summary> '
        ''' An event has been closed '
        ''' </summary> '
        EventCloseVoted = 10
        ''' <summary> '
        ''' A comment has been added to an event '
        ''' </summary> '
        CommentAdded = 11
        ''' <summary> '
        ''' A comment has been updated '
        ''' </summary> '
        CommentUpdated = 12
        ''' <summary> '
        ''' A comment has been deleted '
        ''' </summary> '
        CommentDeleted = 13
        ''' <summary> '
        ''' An event or comment has been reported as spam '
        ''' </summary> '
        SpamReported = 14
    End Enum
End Interface

存储库同样简单

Public Class ActivityLogRepository : Implements IActivityLogRepository
    Private dc As MyAppDataContext
    Public Sub New()
        dc = New MyAppDataContext
    End Sub

    ''' <summary> '
    ''' Adds the activity. '
    ''' </summary> '
    ''' <param name="activity">The activity.</param>
    Public Sub Create(ByVal activity As ActivityLog) Implements IActivityLogRepository.Create
        dc.ActivityLogs.InsertOnSubmit(activity)
    End Sub

    ''' <summary> '
    ''' Gets the activities. '
    ''' </summary> '
    ''' <returns>results AsQueryable</returns>
    Public Function Read() As IQueryable(Of ActivityLog) Implements IActivityLogRepository.Read
        Dim activity = (From a In dc.ActivityLogs
                        Order By a.ActivityDate Descending
                        Select a)
        Return activity.AsQueryable
    End Function

    ''' <summary> '
    ''' Submits the changes. '
    ''' </summary> '
    Public Sub SubmitChanges() Implements IActivityLogRepository.SubmitChanges
        dc.SubmitChanges()
    End Sub
End Class

堆栈跟踪如下

[NullReferenceException:对象引用未设置为对象的实例。] MyApp.Core.Domain.ActivityLogService.AddActivity(Int32 userid, Int32 activity, String ip) 在 E:\Projects\MyApp\MyApp.Core\Domain\Services\ActivityLogService.vb:49 E:\Projects\MyApp\MyApp.Core\Controllers\UsersController.vb:258 中的 MyApp.Core.Controllers.UsersController.Authenticate(String go) lambda_method(闭包,ControllerBase,对象[])+163 System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase 控制器,Object[] 参数)+51 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +409 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 参数) +52 System.Web.Mvc.c__DisplayClass15.b__12() +127 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter 过滤器,ActionExecutingContext preContext,Func1 continuation) +436 System.Web.Mvc.<>c__DisplayClass17.<InvokeActionMethodWithFilters>b__14() +61 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodWithFilters(ControllerContext controllerContext, IList1 过滤器,ActionDescriptor actionDescriptor,IDictionary2 parameters) +305 System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +830 System.Web.Mvc.Controller.ExecuteCore() +136 System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +232 System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +39 System.Web.Mvc.<>c__DisplayClassb.<BeginProcessRequest>b__5() +68 System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +44 System.Web.Mvc.Async.<>c__DisplayClass81.b__7(IAsyncResult _) +42 System.Web.Mvc.Async.WrappedAsyncResult`1.End() +141 System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, 对象标签) +54 System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40 System.Web.Mvc.c__DisplayClasse.b__d() +61 System.Web.Mvc.SecurityUtil.b__0(动作 f)+31 System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(动作动作)+56 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +110 System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult 结果) +38 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +690 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +194

这是附加到调试器时的错误图像。

这是有问题的数据库架构的图像

谁能解释一下我在这里可能遗漏了什么?

【问题讨论】:

  • ActivityLogRepository 是一个静态类吧?您是否还检查过您的 Create 方法中是否一切正常?
  • 我添加了信息以显示存储库的样子

标签: asp.net asp.net-mvc vb.net nullreferenceexception


【解决方案1】:

这可能看起来我很迟钝,但是您是否在第 49 行添加了对所有内容的空检查?

MyApp.Core.Domain.ActivityLogService.AddActivity(Int32 userid, Int32 活动, String ip) in E:\Projects\MyApp\MyApp.Core\Domain\Services\ ActivityLogService.vb:49

从外观上看,这可能是确保 ActivityLogRepository 不为空。

【讨论】:

    【解决方案2】:

    以下是调试/避免 NullReferenceExceptions 的一些一般思路:

    确保一切都已初始化。

    初始化为 null/Nothing 不算数。

    决定什么可以为空,什么不能为空

    通常,代码库的大部分引用在其整个或大部分范围内都不应为空。如果是这样,那你为什么要忍受呢?

    积极减少在某些时候允许为空的引用数量。目标是使只有少数列入白名单的引用可以在空状态下找到。最好在 cmets 或 ReSharper's annotations 之类的内容中明确说明。

    注意在白名单上进行分支并采取相应措施。

    防御性地拒绝空参数

    方法应拒绝未列入白名单的空参数并抛出 ArgumentNullException。这可以立即清楚什么引用是空的,而且很多时候这比 NullReferenceException 更接近空引用的来源,而 NullReferenceException 仅在取消引用站点上抛出。

    处理列入白名单的引用

    如果异常是在列入白名单的引用上引发的,请添加适当的代码来处理它。这应该是一个“if”分支;不要捕获 NullReferenceException。

    所有未列入白名单的空引用都是错误

    如果引发异常的引用未列入白名单,则这是一个错误。您必须将空引用追溯到原点。

    检查引用的初始化位置。如果是变量,可以简单检查包含方法。

    如果是字段,检查构造函数进行初始化。然后寻找对显式空引用(Visual Basic 中为 Nothing)或白名单引用的分配。您可能需要跟踪从一个变量/字段到另一个变量/字段,直到找到空引用的真正来源。

    要修复它,请将其替换为适当的有效参考,或者如果来源是列入白名单的参考,则添加“if”子句。

    【讨论】:

      【解决方案3】:

      @gbs 为我指明了正确的方向。

      我的服务中有以下内容

          Private ActivityLogRepository As IActivityLogRepository
      
          Public Sub New(ByVal ActivityLogRepository As IActivityLogRepository)
              ActivityLogRepository = ActivityLogRepository
          End Sub
      

      不知道我是怎么错过的,改成这个就可以了

          Private ActivityLogRepository As IActivityLogRepository
      
          Public Sub New(ByVal _ActivityLogRepository As IActivityLogRepository)
              ActivityLogRepository = _ActivityLogRepository
          End Sub
      

      【讨论】:

      • 我只是希望调试器能告诉我们“什么”对象未设置为对象的实例。不仅仅是“一个对象”。
      • 不是调试器告诉你的。调试器只是说“嘿,伙计,有一个例外”。异常包含该消息,但它是由运行时生成的,而不是调试器。在异常消息中获取这一点并不容易,因为对于 CLR 而言,未设置的对象只是 IL 堆栈上的一些引用。 CLR 必须跟踪放在堆栈上的所有引用的名称(例如,everything)。如果引用是局部变量,它在发布代码中将没有名称。而 IL 堆栈只是一种抽象,很可能已经被优化掉了。
      最近更新 更多