Конструктор расширенного контроллера не имеет экземпляра пользователя - PullRequest
0 голосов
/ 21 декабря 2018

У меня есть базовый контроллер, который выходит из 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 (Identity) в конструкторе

Ответы [ 2 ]

0 голосов
/ 22 декабря 2018

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

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

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 голосов
/ 22 декабря 2018

Как я уже упоминал в своем комментарии к вопросу, наследование здесь плохой выбор.Вместо этого вы пытаетесь передать неспецифические данные в 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>
}
...