在ASP.NET MVC中向主页面传递数据

102

你在ASP.NET MVC中传递数据给母版页的方式是什么,同时又不违反MVC规则?

个人而言,我更喜欢编写抽象控制器(基础控制器)或基类,并将其传递到所有视图中。


1
我写了一份指南,介绍了我是如何处理这个问题的:http://www.britishdeveloper.co.uk/2010/06/mvc-pass-viewmasterpage-model.html,希望能有所帮助。 - BritishDeveloper
9个回答

77

如果你喜欢将视图数据类强类型化,这可能适合你。其他解决方案可能更加“正确”,但在我看来,这是设计和实用性之间的一个很好的平衡。

母版页采用强类型视图数据类,其中仅包含与其相关的信息:

public class MasterViewData
{
    public ICollection<string> Navigation { get; set; }
}

每个使用该母版页的视图都需要一个强类型视图数据类,其中包含其信息并从母版页视图数据继承:
public class IndexViewData : MasterViewData
{
    public string Name { get; set; }
    public float Price { get; set; }
}

为了让单个控制器不需要知道如何组合主页面数据,我将这个逻辑封装到一个工厂中,并将其传递给每个控制器:

public interface IViewDataFactory
{
    T Create<T>()
        where T : MasterViewData, new()
}

public class ProductController : Controller
{
    public ProductController(IViewDataFactory viewDataFactory)
    ...

    public ActionResult Index()
    {
        var viewData = viewDataFactory.Create<ProductViewData>();

        viewData.Name = "My product";
        viewData.Price = 9.95;

        return View("Index", viewData);
    }
}

继承很好地匹配了主视图关系,但当涉及到渲染部分视图/用户控件时,我会将它们的视图数据组合到页面的视图数据中,例如:
public class IndexViewData : MasterViewData
{
    public string Name { get; set; }
    public float Price { get; set; }
    public SubViewData SubViewData { get; set; }
}

<% Html.RenderPartial("Sub", Model.SubViewData); %>

这只是示例代码,不可直接编译。设计用于ASP.Net MVC 1.0。


4
这是Scott Gutherie推荐的方法,所以我也同意。 - Simon Fox
4
http://weblogs.asp.net/scottgu/archive/2007/12/06/asp-net-mvc-framework-part-3-passing-viewdata-from-controllers-to-views.aspx#5415732 - Dan Atkinson
@Jason:Asp.net MVC允许您指定控制器工厂,这意味着您可以使用依赖注入框架来实例化需要构造函数参数的控制器。where T : MasterViewData, new()告诉编译器Create方法只能被调用扩展了MasterViewData类型(在本例中是IndexViewData)并且使用默认或无参数构造函数的类型。换句话说,我需要能够说MasterViewData data = new T(); - StriplingWarrior
5
我喜欢使用强类型模型进行工作,但我不太喜欢将主数据与其他所有模型和操作耦合在一起。虽然我有点晚参加这个讨论,但我发表了自己的主数据处理方法,可以保持更松散的联系。 - Todd Menier
有人可以帮我理解一下,Navigation属性是在哪里/何时被赋值的吗? - IsmailS
显示剩余2条评论

60

我更喜欢将主视图中的数据驱动部分拆分成部分视图,并使用Html.RenderAction进行渲染。这比流行的视图模型继承方法具有以下几个明显优势:

  1. 主视图数据与“常规”视图模型完全解耦。这是组合优于继承,使系统更松散耦合且更易于更改。
  2. 主视图模型由完全独立的控制器操作构建。 "常规" 操作不需要担心这一点,也不需要视图数据工厂,它似乎过于复杂。
  3. 如果您使用像AutoMapper这样的工具将域映射到视图模型,则会发现更容易配置,因为当视图模型不继承主视图数据时,它们将更接近于您的域模型。
  4. 通过单独的主数据操作方法,您可以轻松地将输出缓存应用于页面的某些区域。通常,主视图包含的数据比主页内容更不经常更改。

3
另一个好处是,你可以根据当前运行状态使用不同的主页面来呈现相同的视图。 - StriplingWarrior
1
我非常喜欢这个答案-其他提出的方法似乎有点过于复杂。 - Paddy
2
在我看来,这是最优雅的解决方案。 - autonomatt
1
这个解决方案对我来说也是最好的。非常感谢! - JimDaniel
1
这是一个很好的方法,但请记住您仍然需要指定“部分操作”的路由。请参阅此答案https://dev59.com/KnE95IYBdhLWcg3wb9db#3553617 - Alex from Jitbit
显示剩余4条评论

20

编辑

Generic Error 提供了一篇更好的答案,请阅读它!

原始回答

微软实际上在"官方"方式上发布了一篇文章来处理这个问题。他们提供了一个逐步介绍并解释其理由的步骤。

简而言之,他们建议使用一个抽象控制器类,但你可以自己看看。


谢谢!那个例子正好是我在做的事情……从数据库中获取每个页面上的类别。 - Martin
Scott Gutherie,MVC 的作者之一,推荐 @Generic Error 提供的解决方案。 - Simon Fox
1
即使你的回答已被提问者正式接受,并被认为是正确的答案,也要给那些指向最佳答案的人+1。 - IsmailS
即使您的答案已被提问者正式接受并采纳,也应该为指向最佳答案而加1分。 - Dave Jellison
实际上,Todd Menier目前是这个问题的最佳答案。 - andreialecu

7

抽象控制器是个好主意,我还没有找到更好的方法。我很想看看别人都做了些什么。


3

2
我发现将所有传递给视图的模型对象都有一个共同的父类非常有用。
毕竟,在页面之间总会存在一些共同的模型属性。

0
其他解决方案缺乏优雅,而且需要太长时间。我很抱歉在将近一年之后才做出这个非常悲伤和贫困的事情。
<script runat="server" type="text/C#">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        MasterModel = SiteMasterViewData.Get(this.Context);
    }

    protected SiteMasterViewData MasterModel;
</script>

很明显,我在SiteMasterViewData上有一个静态方法Get(),它返回SiteMasterViewData。


对于许多人来说,这可能看起来有些不专业或“不干净”,但它可以相当快地完成工作。 - argh
呃,你的代码似乎比使用Html.RenderAction()更难维护。 - Dan Esparza

0

Request.Params对象是可变的。在请求处理周期中,将标量值添加到其中非常容易。从视图的角度来看,这些信息可以在QueryString或FORM POST中提供。希望这有所帮助。


0

我认为另一种好的方式是创建一个视图接口,其中包含某些属性,例如某个接口的父视图,这样您就可以同时用于需要引用页面(父控件)的控件和应从视图访问的主视图。


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