ModelState 在检查 null 时如何/为什么会抛出 System.NullReferenceException?

How/why does ModelState throw System.NullReferenceException when being checked for null?

提问人:Sarov 提问时间:7/28/2023 最后编辑:Sarov 更新时间:8/4/2023 访问量:69

问:

更新:为了避免这个问题,我尝试显式检查null,认为肯定不可能仍然抛出异常。 确实如此。 以下行引发 System.NullReferenceException:

if (ModelState == null)

如何?!


我们收到了一个自动错误报告,显示特定行抛出 NullReferenceException...但我无法弄清楚那条特定的线怎么可能做到这一点。

有问题的行是 。但。。。我那里有一个空条件运算符。它怎么会抛出 NullReferenceException?if (ModelState?.IsValid == false)

我们还没有弄清楚如何故意重现这个错误,但它时不时地发生。

这是代码(略有精简,但相关方法保持不变:)

using AutoMapper;
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace MyApp.Web.Controllers
{
    public class ServiceCallController : Controller
    {
        [NotNull] private readonly IMapper _mapper;
        [NotNull] private readonly IServiceCallService _service;

        public ServiceCallController([NotNull] IMapper mapper, [NotNull] IServiceCallService service)
        {
            _mapper = mapper;
            _service = service;
        }

        [HttpPost]
        public ActionResult Index(ServiceCallViewModel vm)
        {
            if (ModelState?.IsValid == false)//This line throws "System.NullReferenceException: Object reference not set to an instance of an object."
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                Response.TrySkipIisCustomErrors = true;
                return Json(new { message = string.Join("<br/>", ModelState.GetErrorList()) });
            }

            try
            {
                vm.Attachments.RemoveAll(a => a == null);
                var serviceCall = _mapper.Map<ServiceCallDto>(vm);
                _service.SubmitServiceCall(serviceCall);
                return Json(new { url = Url.Action("CallSearch", "Search") });
            }
            catch (Exception ex)
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                Response.TrySkipIisCustomErrors = true;
                return Json(new { message = ex.Message });
            }
        }
    }

    public class ServiceCallDto
    {
        // ...bunch of properties...
    }

    public class ServiceCallViewModel
    {
        public List<HttpPostedFileBase> Attachments { get; set; }
        // ...bunch of properties...
    }

    public interface IServiceCallService
    {
        void SubmitServiceCall(ServiceCallDto serviceCall);
    }

    public static class ModelStateExtensions
    {
        public static List<string> GetErrorList([NotNull] this ModelStateDictionary modelState) =>
            (from item in modelState.Values
             from error in item.Errors
             select error.ErrorMessage).ToList();
    }
}

以下是我们收到的自动错误电子邮件:

User Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Mobile/15E148 Safari/604.1
System.NullReferenceException: Object reference not set to an instance of an object.
   at MyApp.Web.Controllers.ServiceCallController.Index(ServiceCallViewModel vm) in C:\Atlassian\bamboo-home\local-working-dir\CMF-MyApp-JOB1\Web\Controllers\ServiceCallController.cs:line 26
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c.<BeginInvokeSynchronousActionMethod>b__9_0(IAsyncResult asyncResult, ActionInvocation innerInvokeState)
   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_0.<InvokeActionMethodFilterAsynchronouslyRecursive>b__0()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass11_2.<InvokeActionMethodFilterAsynchronouslyRecursive>b__2()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_6.<BeginInvokeAction>b__4()
   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass3_1.<BeginInvokeAction>b__1(IAsyncResult asyncResult)

为了进一步说明该问题:

我的理解是,评估结果为 a aka a .如下面的代码片段所示,即使在 .所以。。。该行怎么可能抛出 NullReferenceException?ModelState?.IsValidbool?Nullable<bool>falsenull

ModelStateDictionary nullState = null;
if (nullState?.IsValid == false)//Unlike what's happening in the real code in the wild, this line does NOT throw an error.
    System.Diagnostics.Debugger.Break();
else if (nullState?.IsValid == true)
    System.Diagnostics.Debugger.Break();
else if (nullState?.IsValid == null)
    System.Diagnostics.Debugger.Break();//The debugger stops on this line.
else
    System.Diagnostics.Debugger.Break();
C# NullReferenceException ModelState

评论

1赞 papafe 8/4/2023
您是如何获得 ModelState 的?它是否可能是一个属性,其 getter 可以引发 NullReferenceException?
0赞 Sarov 8/4/2023
ServiceCallController 继承自 System.Web.Mvc.Controller,后者具有 属性。我看不到属性 getter 的代码,因为它是由 Microsoft 编写的。public ModelStateDictionary ModelState { get; }
0赞 papafe 8/4/2023
您实际上可以看到源代码,但在任何情况下都不应该发生(也可能有不同的堆栈跟踪)。你完全确定你发布的代码就是正在执行的代码吗?难道正在运行的代码是没有空条件运算符的旧版本吗?
0赞 Sarov 8/4/2023
我专门更改了代码以添加检查,因为以前的代码抛出了 NullReferenceException,并且当我这样做时,堆栈跟踪的行号也相应地更改了,所以是的,我敢肯定。if (ModelState == null)ModelState?.IsValid
0赞 Sarov 8/4/2023
看起来 2 年前才添加的可空性?此代码早于此。也许这就是问题所在......我想我明天会考虑更新。ControllerContext

答: 暂无答案