Конструктор расширенного контроллера не имеет экземпляра пользователя

2

У меня есть базовый контроллер, который выходит из Controller, класс работает нормально, но я подумал, что я часто использую код для получения текущего пользователя из базы данных. Поэтому я решил, что должен создать конструктор и переместить код, который я использую в каждой функции. По сути, я хотел подготовить параметры для любого из методов в моем контроллере.

Итак, вот что у меня сейчас (и работает нормально):

public class UsersController : Controller
{
    private DBContext db = new DBContext();

    public ActionResult Info()
    {
        User user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
        return View(user);
    }

    public ActionResult Edit(int? id){
        User user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
        if(user.id == id){
            return View(user);
        }
    }
}

Но моя идея заключалась в том, чтобы создать что-то вроде этого:

public class UsersController : Controller
{
    private DBContext db = new DBContext();
    private User _user;

    public UsersController()
    {
        _user = db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
    }

    public ActionResult Info()
    {
        return View(_user);
    }

    public ActionResult Edit(int? id){
        if(_user.id == id){
            return View(_user);
        }
    }
}

Когда я внес эти изменения, я получаю следующую ошибку:

Ошибка сервера в приложении '/' В экземпляре объекта не задана ссылка на объект.

Описание: во время выполнения текущего веб-запроса произошло необработанное исключение. Пожалуйста, просмотрите трассировку стека для получения дополнительной информации об ошибке и о том, где она возникла в коде.

Сведения об исключении: System.NullReferenceException: ссылка на объект не установлена для экземпляра объекта.

Я попытался отладить и обнаружил, что проблема в том, что мой User имеет значение null когда вызывается конструктор, поэтому я предполагаю, что некоторые другие языки могут вызывать родительский конструктор перед добавлением или после добавления своей собственной настройки, например что-то вроде этого:

public function __Construct($x){
    $this->x = $x
    parent::__construct();
}

или же

public function __Construct($x){
    parent::__construct();
    $this->x = $x
}

Я пытался сделать то же самое в моей программе, используя base, но, похоже, ничего не работает, и это всегда приводит меня к ошибке какой-то другой природы. Я даже не уверен, что это правильный способ сделать это, потому что все, что мне нужно, - это создать моего User (личность) в конструкторе.

  • 1
    Я бы порекомендовал не использовать наследование здесь. Что вы предлагаете, так это то, что каждый контроллер должен либо знать об этом пользователе, либо не знать об этом пользователе. Что происходит, когда вы добавляете что else то else . Затем у вас есть некоторые контроллеры, которые знают о user и некоторые контроллеры, которые знают о else но не оба, потому что вы не можете наследовать от 2 контроллеров.
  • 0
    Кстати, если пользователь не вошел в систему User.Identity или User.Identity.Name имеет значение null.
Показать ещё 3 комментария
Теги:
asp.net-mvc
inheritance
constructor
extends

2 ответа

0

Похоже, пользователь не найден, возможно, потому, что идентификатор пользователя не заполняется в субъекте потока при вызове конструктора для контроллера.

Мое предложение состоит в том, чтобы не тянуть пользовательские данные в конструктор, а вместо этого захватывать их, когда они вам нужны. Чтобы избежать дублирования кода, вы можете написать защищенный или закрытый метод (не метод действия), чтобы получить его:

public class UsersController : Controller
{
    private DBContext db = new DBContext();

    private User GetCurrentUser()
    {
        return db.Users.Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault();
    }

    public ActionResult Info()
    {
        var user = GetCurrentUser();
        return View(user);
    }

    public ActionResult Edit(int? id){
        var user = GetCurrentUser();
        if(user.id == id){
            return View(user);
        }
    }
}
  • 0
    Я собирался использовать ваш подход. Дело в том, что я упростил задачу, чтобы опубликовать это здесь. Мне нужно инициализировать больше переменных, а не только пользователя, и это заставляет меня сталкиваться с большим количеством кода в каждом методе. Например: User user = GetCurrentUser(); Department department = GetCurrentDepartment(); ... так что я получаю то же самое, что и в начале. Тем не менее, это хороший способ сделать код более читабельным, и это дает вам дополнительные очки.
0

Как я уже упоминал в своем комментарии к вопросу, наследование здесь плохой выбор. Вместо этого вы пытаетесь передать неспецифические данные в View. Лучший выбор - использовать ActionFilter.

Нам нужен класс для хранения информации о пользователях для представления:

public class UserInfo
{
  public bool HasUser { get; set; }
  public User User { get; set; }
}

Нам нужно место для хранения данных, которые не являются специфичными для представлений. Я предпочитаю использовать ViewData (потому что этот маршрут предоставляет строго типизированные данные и простой способ отладки этого места хранения):

public static class ViewDataExtensions
{
   private const string UserInfoKey ="_UserInfo";

   public static void GetUserInfo(this ViewData viewData)
   {
     return viewData.ContainsKey(UserInfoKey)
       ? viewData[UserInfoKey] as UserInfo
       : null;
   }

   public static UserInfo SetUserInfo(this ViewData viewData, UserInfo userInfo)
   {
     viewData[UserInfoKey];
   }
}

Далее нам нужен способ, чтобы заполнить эту информацию, когда это необходимо

public class AddUserToViewDataFilterAttribute : ActionFilterAttribute
{
    private DBContext db = new DBContext();

    public void OnActionExecuting(ActionExecutingContext context)
    {
      var user = context.Controller.User;

      var userInfo = new UserInfo
      {
        HasUser = !string.IsNullOrEmpty(User.Identity?.Name),
        User = !string.IsNullOrEmpty(User.Identity?.Name)
          ? db.Users
        .Where(m => m.username.Equals(User.Identity.Name)).FirstOrDefault()
          : null;
      };

      context.ControllerContext.ViewData.SetUserInfo(userInfo);
    }
}

Заполните его при необходимости:

public class MyController
{
  public ActionResult DoesNotNeedUserInfo()
  {
  }

  [AddUserToViewDataFilter]
  public ActionResult NeedsUserInfo()
  {
  }
}

По мнению:

@model <whatever>
@if (ViewData.GetUserInfo().HasUser) {
  <div>@ViewData.GetUserInfo().User.Name</div>
}
  • 0
    Спасибо, что нашли время, чтобы помочь мне. У меня нет большого опыта работы с фильтрами, я думаю, что я использовал их только для создания собственной системы авторизации в своей программе и сделал это после обучения. Я до сих пор не уверен, как переменная будет создана и использована внутри метода. И потом, что если мне нужно создать Department department переменной второй переменной на основе пользователя, которого я получаю, и мне также понадобится эта переменная department доступная любым методом, понадобится ли мне второй фильтр?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню