У меня есть базовый контроллер, который выходит из 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
(личность) в конструкторе.
Похоже, пользователь не найден, возможно, потому, что идентификатор пользователя не заполняется в субъекте потока при вызове конструктора для контроллера.
Мое предложение состоит в том, чтобы не тянуть пользовательские данные в конструктор, а вместо этого захватывать их, когда они вам нужны. Чтобы избежать дублирования кода, вы можете написать защищенный или закрытый метод (не метод действия), чтобы получить его:
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);
}
}
}
User user = GetCurrentUser(); Department department = GetCurrentDepartment();
... так что я получаю то же самое, что и в начале. Тем не менее, это хороший способ сделать код более читабельным, и это дает вам дополнительные очки.
Как я уже упоминал в своем комментарии к вопросу, наследование здесь плохой выбор. Вместо этого вы пытаетесь передать неспецифические данные в 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>
}
Department department
переменной второй переменной на основе пользователя, которого я получаю, и мне также понадобится эта переменная department
доступная любым методом, понадобится ли мне второй фильтр?
else
тоelse
. Затем у вас есть некоторые контроллеры, которые знают оuser
и некоторые контроллеры, которые знают оelse
но не оба, потому что вы не можете наследовать от 2 контроллеров.User.Identity
илиUser.Identity.Name
имеет значение null.