更新:为了避免这个问题,我尝试显式检查 null,认为肯定不可能仍然抛出异常。 确实如此。 以下行抛出 System.NullReferenceException:
if (ModelState == null)
如何?!
我们收到了一份自动错误报告,显示特定行正在抛出 NullReferenceException...但我无法弄清楚如何该特定行可能会这样做。
有问题的线路是
if (ModelState?.IsValid == false)
。但是...我那里有一个空条件运算符。它怎么会抛出 NullReferenceException?
我们无法弄清楚如何故意重现该错误,但它时不时地发生。
这是代码(稍微精简,但相关方法保持不变:)
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)
进一步说明问题:
我的理解是
ModelState?.IsValid
评估为 bool?
又名 Nullable<bool>
。正如下面的代码片段所示,哪个 c# 能够与 false
进行比较,即使在 null
时也是如此。那么...该行怎么可能抛出 NullReferenceException?
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();