MVC - 将附加信息传递给视图

3
我正在使用Asp.Net MVC框架开发网站。我们需要在一些页面上添加通用用户信息,类似于stack overflow顶部的声望栏。
我认为,在创建控制器方法时不应增加任何开销以将此额外信息添加到视图中。这排除了通过ViewData对象传递此信息的选项,或更改ViewModels以接受声誉字段的选项,因为这将导致每个控制器看起来都像这样:
public ActionResult Index()
{
    ViewData["reputationScore"] = GetUsersReputation(userId);

    // main controller logic here
}

如果在网站的90%页面上都使用了这个功能,那么想要显示用户徽章计数也将非常耗时,需要进行大量修改。
我能想到4种解决方案:
1、使用Master页面。 在Master页面代码后端中检索用户信誉度,并在Master页面标记中添加信誉度的标记。
缺点: - 这似乎偏离了MVC的所有特点。 - 我一直在考虑转向使用替代视图引擎(例如razor),我不确定它们能否很好地混合。 - 它往往会限制声望的布局 - 很难把它放在页面中间。
2、扩展HtmlHelper以添加GetUsersReputation方法。 这似乎是对Html对象使用的轻微违规——它不仅渲染输出,而且还会访问数据库。除此之外,我无法想到任何其他重大问题。
3、覆盖System.Web.Mvc.ViewPage。 重写Viewpage以公开一个对象,除Html外,该对象还可用于调用访问数据库的一系列方法。确保可以像添加HtmlHelper那样添加扩展方法,以便在需要新方法时可以适当地扩展它。这可能会使我们编写类似以下内容的内容:
4、使用部分类和扩展方法。 通过创建存储器实现与视图分离。在存储器中添加扩展方法以处理与数据库的交互。然后使用部分类将它们绑定到视图上。这种方法使视图保持简单,并使存储器具有最大灵活性。
<% DataAccess.GetUsersReputation() %>

创建一个基础通用视图模型 不要直接将视图模型传递给视图,而是将其包装在一个基础视图模型中,该模型可以保存所有所需的方法:

public ActionResult Index()
{
    MyViewModel viewCoreInfo = model.GetData();

    return View(new BaseViewModel<MyViewModel>(viewCoreInfo));
}

BaseViewModel可以公开您在网页上需要的所有属性,例如UsersReputation(它可以在构造函数中查询数据库,或在访问该属性时加载数据)。我认为这更好地维护了MVC隐喻,但有点繁琐。

  1. 是否有其他人提出了更好的解决方案?
  2. 哪个是最好的 - 如果您使用过它们,它们是否有效/存在问题?
5个回答

3

为什么不让MyViewModel继承BaseViewModel呢?这样你就可以使用BaseController.OnResultExecuting来填充公共值:

protected override void OnResultExecuting(ResultExecutingContext ctx) {
    base.OnResultExecuting(ctx);
    var baseView = ctx.Result as BaseViewModel;
    if (baseView != null)
    {
        //assign values here
    }
}

感谢您的回答。我喜欢覆盖OnResultExecuting方法的想法。我的担心是让MyViewModel继承自BaseViewModel,这会在两个类之间引入耦合。如果您决定稍后另一组页面需要显示好友数量,但仍在使用MyViewModel作为主要数据,则需要允许BaseViewModel实现好友和声望方法。而采用我的解决方案,您只需将BaseViewModel更改为FriendDataBaseViewModel即可。感谢您的帮助 - 我现在会研究OnResultExecuting。 - Giles Smith
好的。创建一个名为IMasterPageData的接口。让你的BaseViewModelFriendDataBaseViewModel实现它。然后只需将OnResultExecuting中的检查更改为IMasterPageData即可。 - jgauffin

3
由于您提到了可能转向剃刀视图引擎,我认为ScottGu的这篇博客文章可能会引起您的兴趣。
他讨论了在剃刀上使用“部分”,它将允许您在布局(“主页”)上创建一个可以加载额外内容的部分。以这种方式创建SO风格的信息栏将是干净而简单的。
编辑:
至于根据下面的评论呈现部分视图...新的mvc音乐商店教程有一些示例。
在这种情况下,他们使用Html.RenderAction。 Html.RenderPartial是另一个选项。 这个问题中概述了区别,但我相信网上还有其他资源。
您的控制器只需返回一个部分视图即可:
 return PartialView();

Sections就像Webforms引擎中的ContentPlaceHolders一样。它并没有真正解决OP所面临的问题。OP想知道将要放置在视图/部分/任何地方的信息应该如何从控制器传输。 - jgauffin
@Giles 我明白你的意思。如果该部分呈现一个调用自己控制器/操作的partialview或类似内容,会怎样呢? - stephen776
@Stephen776 如果这是可能的话,那听起来像是个好方法。你可以从局部视图中调用控制器吗?你有在其他地方见过这样的例子吗? - Giles Smith
@Giles 是的,我相信它就像 Html.RenderPartial("action", "controller", "other params(optional)") 这么简单。我知道我在我的开发机器上有一个示例。当我找到它时,我可以发布一个例子。 - stephen776
@Giles 在上面更新了答案并提供了更多信息。这绝对是可行的,而且在我看来是一个干净的解决方案。 - stephen776
显示剩余2条评论

1

您可以创建操作过滤器,重写OnResultExecuting方法,填充常见值并将其添加到您的操作/控制器中:

public class AddCommonData : ActionFilterAttribute 
{
  public override void OnResultExecuting(ResultExecutingContext filterContext)
  {
    ViewResult viewResult = filterContext.Result as ViewResult;
    if (viewResult != null)
    {
      //...
    } 
  }
}

0

我已经做了这个,我创建了一个基础模型类,其中包含所有共同的信息。我的其他视图模型类都继承自这个基础类。但是你必须在每个控制器中填充这些信息。这是我选择的解决方案。


0

你可以创建带有 CodeBehind 文件的部分视图,其中你将填充部分视图数据。这是一种非 MVC 的方法,但它会起作用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接