NRE на переменных класса MVC controller'а

124
29 ноября 2019, 02:40

Есть проект WebForms (legacy) + ASP.NET MVC + SimpleInjector

public abstract class CustomBaseController : Controller
{
    protected User user;
    protected IAppDbContext repo;
    public CustomBaseController(User user, IAppDbContext repo)
    {
        this.user = user;
        this.repo = repo;
    }
}
// my controller
[MyCustomAuthorize]
public class MyCustomController : CustomBaseController
{
    public MyCustomController(User user, IAppDbContext repo) : base(user, repo)
    {
    }
    [HttpPost]
    public ActionResult PostMethod()
    {
         try
         {    
            // Какое-то обращение к полю user
            // Получаю NullReferenceException 
            user.Name = "NewUserName";
         }
         catch (Exception ex)
         {
            // логирование
         }
    }
}
// user entity
public class User
{
    public Guid Id { get; protected set; }
    public string Email { get; protected set; }
    public string DisplayName { get; protected set; }
    public string FirstName { get; protected set; }
    public string LastName { get; protected set; }
    // and other properties
}
// Инициализация DI
private static void InitializeContainer()
{
    var container = new Container();
    container.Register<IUserSessionManagement, UserSessionManagement >(Lifestyle.Scoped);
    container.Register<User>(() => container.GetInstance<UserSessionManagement>().UserSession, Lifestyle.Scoped);
    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}
// Wrapper for User 
public class UserSessionManagement : IUserSessionManagement
{
    public User UserSession { get; }
    public UserSessionManagement(IAppDbContext repo)
    {
        UserSession = (HttpContext.Current.Session[SESSION_AUTHENTICATED_USER] as User) ?? new User(Guid.Empty, null, null);
    }
}
  • Авторизация на куках
  • В качестве DI библиотеки используется SimpleInjector. Он не может зарезолвить зависимость, которая была бы null.
  • Первое что пришло в голову. Вдруг где-то в контроллере переписываю ссылку. Я поискал все места использования поля user и везде только запрашиваю, а пишут только 1 раз в конструкторе CustomBaseController.
  • К сожалению я не могу это воспроизвести в дебаге. Наличие проблемы известно из логов.

1) Пробовал подождать пока сессия экспарируется.

2) Пробовал залогиниться. Открыть еще одну вкладку в браузере. Выйти из первой. Сделать запрос со второй (ничего).

3) Сымитировать долгий запрос через thread sleep (но это фигня по-моему)

NOTE: Спрашивал на англоязычном SO, но мой английский слаб и у меня не получилось с первого раза объяснить проблему, когда к ней еще был интерес.

UPD:

Текст ошибки получается таким, где UserID пустой, потому что там через null propagation сделано. Соответственно user был null.

An exception occured. Error id: c90dfe71-c608-4412-9805-c1cb9bf92bc6. UserID:;

Полное содержание вызываемого метода.

[HttpPost]
public ActionResult PostMethod()
{
    try
    {
        var provider = repo.GetProviderInfo(user.Id);
        return Json(provider);
    }
    catch (Exception ex)
    {
        var internalErrorId = Guid.NewGuid();
        MSSQLLogger.Instance.GetLogger(MethodBase.GetCurrentMethod())
            .Error($"An exception occured. Error id: {internalErrorId}. UserID:{user?.Id};", ex);
        return new HttpStatusCodeResult(HttpStatusCode.InternalServerError, $"Error id: {internalErrorId}");
    }
}
Answer 1

В общем, что бы я посоветовал:

// Инициализация DI
private static void InitializeContainer()
{
    var container = new Container();
    container.Register<IUserSessionManagement, UserSessionManagement>(Lifestyle.Scoped);    
    DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
}

Базовый класс

public abstract class CustomBaseController : Controller
{
    protected IUserSessionManagement SessionManagement;
    protected IAppDbContext repo;
    public CustomBaseController(IUserSessionManagement sessionManagement, IAppDbContext repo)
    {
        this.SessionManagement = sessionManagement;
        this.repo = repo;
    }
}

Котроллер

// my controller
[MyCustomAuthorize]
public class MyCustomController : CustomBaseController
{
    public MyCustomController(IUserSessionManagement sessionManagement, IAppDbContext repo) : base(sessionManagement, repo)
    {
    }
    [HttpPost]
    public ActionResult PostMethod()
    {
        try
        {
            var user = SessionManagement.UserSession;
            // Какое-то обращение к полю user
            // Получаю NullReferenceException 
            user.Name = "NewUserName";
        }
        catch (Exception ex)
        {
            // логирование
        }
    }
}
Answer 2

Проблема оказалась в следующем: так как все хранилось в сессии и после перезагрузки IIS (или обновления web.config) там ничего не лежит. В сессию информация о юзере попадает после того как происходит обращение к любой странице написанной на WebForm, то бишь отрабатывает логика Admin.Master, но т.к. присутствует лапша из разных технологий, то при прямом обращении через ajax запрос логика из Admin.Master не отрабатывала. Проще говоря забыли добавить это в кастомные атрибуты авторизации.

READ ALSO
Регистронезависимый switch

Регистронезависимый switch

Есть у меня фрагмент кода в котором я хочу заменить if на switch:

127
Реализация превью чужого окна

Реализация превью чужого окна

Как реализовать превью чужих окон в своём окне? Аналогично нажатию alt+tab

96
Ошибка миграции There is already an object named &#39;Permission&#39; in the database

Ошибка миграции There is already an object named 'Permission' in the database

Не понимаю, постоянно ловлю ошибки например

123