Я уже дважды видел, что NullReferenceException
зарегистрировался из веб-приложения Production ASP.NET MVC 4 и записал неверную строку. Неправильно ли строка или две (например, вы получили бы несоответствие PDB), но ошибочно по длине всего действия контроллера. Пример:
public ActionResult Index()
{
var someObject = GetObjectFromService();
if (someObject.SomeProperty == "X") { // NullReferenceException here if someObject == null
// do something
}
// about 40 more lines of code
return View(); // Stack trace shows NullReferenceException here
}
Это произошло дважды для действий на одном контроллере. Второй случай был зарегистрирован на
// someObject is known non-null because of earlier dereferences
return someObject.OtherProperty
? RedirecToAction("ViewName", "ControllerName")
: RedirectToAction("OtherView", "OtherController");
Это очень тревожно. NullReferenceException
очень легко исправить, как только вы знаете, в какой строке оно происходит. Это не так просто, если бы исключение могло произойти в любом месте действия контроллера!
Кто-нибудь когда-либо видел что-то подобное в ASP.NET MVC или в другом месте? Я готов поверить в это разницу между сборкой Release и сборкой Debug, но все же, чтобы отключиться на 40 строк?
EDIT:
Чтобы быть ясным: я автор оригинала Что такое исключение NullReferenceException и как его исправить?". Я знаю, что такое NullReferenceException
. Этот вопрос связан с тем, почему трассировка стека может быть настолько далека. Я видел случаи, когда трассировка стека отключена на строку или две из-за несоответствия PDB. Я видел случаи, когда нет PDB, поэтому вы не получаете номера строк. Но я никогда не видел случая, когда трассировка стека отключена на 32 строки.
ИЗМЕНИТЬ 2:
Обратите внимание, что это произошло с двумя отдельными действиями контроллера внутри одного контроллера. Их код сильно отличается друг от друга. Фактически, в первом случае NullReferenceException
даже не встречался в условном выражении: это было примерно так:
SomeMethod(someObject.SomeProperty);
Был некоторый шанс, что код был реорганизован во время оптимизации, так что фактический NullReferenceException
стал ближе к return
, и PDB фактически был только выключен несколькими строками. Но я не вижу возможности переупорядочить вызов метода таким образом, чтобы код мог перемещаться на 32 строки. Фактически, я просто посмотрел на декомпилированный источник, и он, похоже, не был перестроен.
Что общего у этих двух случаев:
- Они встречаются в одном контроллере (пока)
- В обоих случаях трассировка стека указывает на оператор
return
, и в обоих случаяхNullReferenceException
произошло в 30 или более строках от оператораreturn
.
ИЗМЕНИТЬ 3:
Я только что сделал эксперимент - я только что перестроил решение, используя конфигурацию сборки "Производство", которую мы развернули на наших производственных серверах. Я запускал решение на своем локальном IIS без изменения конфигурации IIS.
Трассировка стека показала правильный номер строки.
EDIT 4:
Я не знаю, является ли это актуальным, но обстоятельство, вызывающее NullReferenceException
, столь же необычно, как и сам номер "неправильной строки". Похоже, мы теряем состояние сеанса без уважительной причины (никаких перезагрузок или чего-то еще). Это не слишком странно. Странная часть заключается в том, что наш Session_Start должен перенаправляться на страницу входа в систему, когда это произойдет. Любая попытка воспроизвести потерю сеанса вызывает перенаправление на страницу входа. Впоследствии, используя кнопку браузера "Назад" или вручную введя предыдущий URL-адрес, вы вернетесь на страницу входа в систему, не нажимая на соответствующий контроллер.
Так что, может быть, две странные проблемы - действительно одна очень странная проблема.
РЕДАКТИРОВАТЬ 5:
Мне удалось получить файл .PDB и посмотреть на него с dia2dump. Я думал, что возможно, что PDB перепутался, и для метода была только строка 72. Это не так. Все номера строк присутствуют в PDB.
EDIT 6:
Для записи это снова произошло в третьем контроллере. Трассировка стека указывает прямо на оператор возврата метода. Этот оператор возврата просто return model;
. Я не думаю, что для этого есть способ .
Фактически, я просто более внимательно посмотрел на журнал и нашел несколько исключений, которые не являются NullReferenceException
, и которые по-прежнему имеют точку трассировки стека в инструкции return
. Оба эти случая находятся в методах, вызванных действием контроллера, а не непосредственно в самом методе действий. Один из них был явно брошен InvalidOperationException
, а один из них был простым FormatException
.
Вот несколько фактов, которые я до сих пор не считал актуальными:
-
Application_Error
в global.asax - это то, что заставляет эти исключения регистрироваться. Он выбирает исключения с помощьюServer.GetLastError()
. - Механизм регистрации регистрирует трассировку сообщений и стека отдельно (вместо записи
ex.ToString()
, что было бы моей рекомендацией). В частности, трассировка стека, о которой я просил, происходит отex.StackTrace
. -
FormatException
был поднят вSystem.DateTime.Parse
, вызванном изSystem.Convert.ToDate
, вызванным из нашего кода. Строка трассировки стека, указывающая на наш код, - это строка, указывающая на "return model;
".