【问题标题】:Extended Controller constructor does not have an instance of User扩展控制器构造函数没有用户实例
【发布时间】:2019-05-22 06:33:35
【问题描述】:

我有一个从 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);
        }
    }
}

当我进行这些更改时,我收到以下错误:

“/”应用程序中的服务器错误。 对象引用未设置为对象的实例。

说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其源自代码的位置的更多信息。

异常详细信息:System.NullReferenceException:对象引用未设置为对象的实例。

我尝试调试,我发现问题是我的 User 在调用构造函数时是 null ,所以我猜测,其他一些语言可以在添加自己的自定义之前或之后调用父构造函数,例如:

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

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

我尝试在我的程序中做同样的事情,使用 base,但似乎没有任何效果,它总是导致我出现其他性质的错误。 我什至不确定这是不是正确的方法,因为我所需要的只是在构造函数中创建我的 User(身份)

【问题讨论】:

  • 我建议在这里使用继承。你的建议是每个控制器都应该知道这个用户,或者不知道这个用户。添加 else 时会发生什么。然后你有一些了解user 的控制器和一些了解else 的控制器,但不是两者都知道,因为你不能从 2 个控制器派生。
  • 顺便说一句,如果用户未登录 User.IdentityUser.Identity.Name 为空。
  • 用户身份原则直到初始化过程的后期才被填充,因此它在控制器构造函数中不可用。
  • 尝试将构造函数更改为 public UsersController() : base()
  • 我使用了你的建议 tatmanblue,但它没有用,我猜,它与 Nkosi 所说的有关,我会无法使用继承来完成我想要的。如果我只创建一个私有 void 方法 Init() 来定义我的控制器添加的私有变量并在每个方法中调用它会不会很好?不优雅,但我认为它可以完成这项工作。我仍然希望我的变量在我的所有方法中都可用,而不是每次都手动定义它们。

标签: c# asp.net-mvc inheritance constructor extends


【解决方案1】:

正如我在问题评论中提到的,继承在这里是一个糟糕的选择。相反,您尝试做的是将非特定数据提供给视图。更好的选择是使用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 变量,我需要第二个过滤器吗?
【解决方案2】:

听起来好像找不到用户,可能是因为调用控制器的构造函数时,线程主体上没有填充用户身份。

我的建议是避免在构造函数中提取用户数据,而是在需要时获取它。为避免重复代码,您可以编写受保护或私有方法(不是操作方法)来获取它:

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(); ...所以我最终得到了与开始时相同的结果。尽管如此,这是使代码更具可读性并给您加分的好方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-05
  • 2017-02-27
  • 2016-06-28
  • 1970-01-01
  • 1970-01-01
  • 2012-01-09
  • 1970-01-01
相关资源
最近更新 更多