【问题标题】:MVC BaseController and BaseModel to ensure that values are available to all viewsMVC BaseController 和 BaseModel 确保值可用于所有视图
【发布时间】:2015-08-27 05:37:25
【问题描述】:

这是关于@Andre Calil 在以下 SO 中提供的解决方案的问题 Razor MVC, where to put global variables that's accessible across master page, partiview and view?

我正在使用 Andre 的方法,但有一个小问题: 我的 _Layout 被强类型化为 BaseModel,而我的视图被强类型化为从 BaseModel 继承的 AccountModel。

问题是:如果我返回一个没有模型的视图,即返回 View(),那么我会得到一个异常。这是因为 BaseController OnActionExecuted 事件正在检查提供的模型是否为空,如下所示:

protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
    if (filterContext.Result is ViewResultBase)//Gets ViewResult and PartialViewResult
    {
        object viewModel = ((ViewResultBase)filterContext.Result).Model;

        if (viewModel != null && viewModel is MyBaseModel)
        {
            MyBaseModel myBase = viewModel as MyBaseModel;
            myBase.Firstname = CurrentUser.Firstname; //available from base controller
        }
    }
    base.OnActionExecuted(filterContext);//this is important!
}

模型为空,因此这种情况并不总是有效。我的下一步是确保始终将模型传递到视图中,即使它是一个空的 BaseModel 对象。问题是我得到以下异常:

The model item passed into the dictionary is of type 'MyNamespace.BaseModel', but this dictionary requires a model item of type 'MyNamespace.AccountModel'.

我需要澄清两点:

  1. 我认为这会起作用,因为 AccountModel 是 BaseModel 的子类?
  2. 如果上面代码中的模型为空,是否有另一种方法可以将模型注入每个视图,这样我就可以避免重构所有代码以包含返回视图(BaseModel.Empty)?

【问题讨论】:

  • 顺便说一句,您是否考虑过为此使用配置文件提供程序?
  • @ChrisHardie 我们最初使用了 Profile 提供程序,但这并不是在所有情况下都有意义,因为需要从构成请求/响应上下文的各个地方提取更多信息.有时这可能包括来自用户的输入
  • @Jacques 一个空视图模型(例如View())应该可以正常工作。您能否在问题中添加您遇到的错误?
  • @Jacques 很抱歉洪水泛滥,还有一点:如果您想选择将空视图模型发送到您的视图,您必须确保它们支持它。也就是说,在您的_Layout 和子视图中,您必须在使用模型之前测试模型是否为空。否则,你会时不时遇到NullReferenceException。

标签: asp.net-mvc


【解决方案1】:

您需要查看 Phil Haacked 在本文中描述的自定义剃须刀视图:

http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx

所以基本上在您的 BaseController 中,您将设置一个公共变量,该变量将在基本控制器的 Initialize 事件中的每个请求中获取(在我的情况下,它是 User 的实例):

public class BaseController : Controller
{
   public User AppUser;

   protected override void Initialize(RequestContext requestContext)
   {
       base.Initialize(requestContext);
       AppUser = _userService.GetUser(id);
       ViewBag.User = AppUser;
   }
}

所以现在你有了一个变量,任何从基础控制器继承的控制器都可以访问它。剩下要做的就是弄清楚如何在视图中使用这个变量。这就是我上面链接的文章将为您提供帮助的地方。默认情况下,您的所有视图都是从 System.Web.Mvc.WebViewPage 生成的。但是,您可以通过执行以下操作来自定义此类的实现:

namespace YourApplication.Namespace
{
   public abstract class CustomWebViewPage : WebViewPage
   {
      private User _appUser;

      public User AppUser
      {
         get
         {
            try
            {
                _appUser = (User)ViewBag.User;
            }
            catch (Exception)
            {
                _appUser = null;
            }
            return _appUser;
         }
      }
   }

   public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> where TModel : class
   {
      private User _appUser;

      public User AppUser
      {
          get
          {
              try
              {
                 _appUser = (User)ViewBag.User;
              }
              catch (Exception)
              {
                 _appUser = null;
              }
              return _appUser;
          }
      }
   }
}

您刚刚定义了一个自定义剃须刀视图类,它具有 user 属性,并尝试从我们在基本控制器中设置的 ViewBag.User 中获取该属性。剩下要做的就是告诉您的应用在尝试生成视图时使用此类。您可以通过在 VIEWS web.config 文件中设置以下行来做到这一点:

<pages pageBaseType="YourApplication.Namespace.CustomWebViewPage">

现在,在您的视图中,您会获得一个用于 User 属性的助手,您可以像这样使用它:

@AppUser

请注意页面声明需要进入 VIEWS web.config 文件而不是主应用程序 web.config!

我认为这对您来说是一个更好的解决方案,因为您不必通过视图模型为所有视图提供基本模型。视图模型应该保留它的用途。

【讨论】:

  • 谢谢。在我们有时间进行适当的实施之前,我们已经暂时删除了所有这些内容,届时我会回复您的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-16
  • 2012-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多