什么是 Asp.net MVC 页面构建的最佳实践,允许并行性和效率?

Posted

技术标签:

【中文标题】什么是 Asp.net MVC 页面构建的最佳实践,允许并行性和效率?【英文标题】:What are the best practices for Asp.net MVC page construction, allowing for parallelism and efficiency? 【发布时间】:2012-01-25 17:37:51 【问题描述】:

在这篇对 ASP.net MVC 页面组合框架的批评中,我发现很难与任何东西争论。

http://www.matlus.com/problems-with-asp-net-mvc-framework-design/

特别是以下几点:

无权访问视图或部分视图实例 ViewData 是您松散类型的信息载体 控制器没有真正控制 子操作对请求上下文没有意义 视图与控制器耦合

对于小型应用程序,我认为其中的很多问题都不是什么大问题,但在您希望重用大量共享组件的大型应用程序中,或者即使您只有一个大型应用程序依赖于多个后端信息源来获取渲染视图所需的所有信息,它开始崩溃。

已经提出了针对这些问题的各种半解决方案,但它们似乎无法很好地扩展或具有不良的设计约束。

这是一个示例应用场景:

    50% 的页面内容在应用程序内的所有页面(页眉、页脚、菜单等)中是通用的 您的应用程序实际上可能由多个区域组成,每个区域都有自己的控制器等,用于独立开发。 常见页面内容中的许多页面元素(菜单、页眉信息、页脚、披露)需要一个或多个服务调用来填写数据以进行呈现。

好的,所以在 asp.net mvc3 中,假设您决定要共享一个包含 50% 通用 UI 标记的通用 Razor 布局。这有助于关注点分离,因为应用程序开发人员无需关注常见的用户界面,而可以专注于特定于其专业领域的逻辑和视图。

但是,如果此共享布局需要数据(一种或多种模型类型的某种外观)来完全呈现自己,这将完全崩溃。您可能在页面上有独立的元素,每个元素都需要特定的数据模型,例如: * 主菜单模型 * 二级菜单模型 *页脚链接模型 * 授权模式 * 页脚免责声明模型

这些模型中的每一个都可能有不同的来源。因此,尽管您可以共享模板,但没有一种简单的方法可以共享构建每个模型的逻辑——而且绝对没有我见过的通用、可扩展和高性能的方法。

我见过的一些解决这个问题的方法是:

强类型通用布局,这要求所有视图模型子类化一个通用的基本模型类。 (但是没有通用的解决方案来填充这样的元模型,这会限制设计并使模型变得庞大且难以测试)此外,模型填充仍然落在每个控制器上,违反了关注点分离和单一责任原则和除了视图特定的模型信息之外,通过堆积大量额外的逻辑来填充元模型,从而使单元测试控制器复杂化。 保持通用布局无类型,因此您不必从通用基础模型继承,但这需要您使用 ViewData 或 ViewBag 来传达模板所需的所有不同模型,因此您会丢失强大的打字优势并最终获得松散的数据合同。您仍然会遇到缺乏用于填充元模型以及随之而来的所有问题的通用解决方案的问题。 每个控制器都必须继承一个通用的基本控制器类以支持通用的布局和模型。构建元模型的共同方面的逻辑在这里。但这并不总是理想的架构或设计约束。这至少解决了关注点分离问题。 代替元模型,在您的公共布局中通过 RenderAction() 使用子操作来制作可重用的“portlet”样式小部件,每个小部件都独立地知道如何构建其数据模型并将其提供给他们的看法。这对分离关注点非常有用,但也有其自身的缺点:视图在渲染期间通过子操作有效地进行服务调用,子操作完全不知道原始请求上下文,违反 DRY 原则,因为每个子操作都不知道之前发生了什么,所以每个人都可以在同一个 http 请求和其他请求中一遍又一遍地进行相同的服务调用。想象一下,一个页面的 20-30 个元素都需要独立调用 RenderAction()...

在其他情况下(也可以在 *** 上看到),RenderAction() 作为解决方案存在其他问题。例如在循环中发出多个 RenderAction() 调用会导致所有这些控制器方法的串行执行。 RenderAction() 没有机会进行并行处理。每个子控制器操作中的 I/O 绑定服务调用会导致整个渲染过程等待 I/O。控制器只知道它的直接视图和模型,没有任何东西可以完整地了解视图内部的内容以并行化某些操作。

上述评论的作者在 ASP.Net mvc 之上开发了一个不同的 UI 模型,称为 Quartz,它允许上帝控制器对视图有深入的了解,并且可以为每个视图模型提供一个视图模型,从而有机会并行化服务调用在一个中心位置构建这些视图模型。我不知道这是否是为克服问题提供钩子的最佳设计,但看起来很有希望。

我的问题是,在 ASP.Net MVC 之上构建复杂应用程序以彻底解决这些问题的最佳实践是什么?我已经想到了几种可能性(尽管在 ASP.Net MVC 中没有一种可能是实用的——那就是待定),但其他人一定已经遇到过这种情况。 ASP.Net MVC 中的设计模式是什么,或者是什么让这个问题变得容易处理?

【问题讨论】:

我建议你查看 asp.net MVC 期货项目aspnet.codeplex.com/releases/view/58781。我还建议调查像果园这样的 CMS:orchard.codeplex.com,看看他们是如何解决这些问题的。 这个问题的范围比较大..更多的讨论话题..见FAQ.. @Dessus,感谢您的指点。我熟悉 Orchard 以及 Kooboo CMS 在 MVC 中所做的一些事情。果园有一些好东西。我正在梳理 Orchard 文档和代码,看看它们是否具有聚合和优化调用的能力。他们注入了自己的视图引擎并使用自定义剃须刀视图子类来实现一些魔力。他们对大多数视图模型和数据类型使用动态类型,这可能是第五个选项,而不是强类型视图。不知道我对此有何感受。更多内容... @core24 对于强类型视图,我同意 +1。这是我不喜欢许多 asp.net 方法(非强类型关系)的东西。 【参考方案1】:

我个人认为通过 RenderAction 使用 Child Actions 的好处大于坏处。

您可以创建“小部件”类型的元素,并将它们的逻辑封装在控制器操作中 - 这样调用小部件的视图可以完全不知道子操作正在做什么以及它是如何做的 - 导致很好的关注点分离。

您已经详细说明了这种方法的缺点,但是我认为可以通过合理的缓存策略将负面影响降到最低。

【讨论】:

这个问题不是缓存可以完全解决的。缓存只会帮助干掉你的动作调用,但如果你有单独的动作需要调用一些服务调用来获取数据,那么对于任何非常复杂的布局来说,这将很快变得低效。例如如果我必须调用 REST 调用来检索 30 条托管内容,我没有机会批量处理这些内容(缓存无济于事,因为它们都是不同的内容 ID),或者如果我需要计算授权规则,我不会不想一次做一件,而是一次做完。【参考方案2】:

我不确定我真的可以为这个“问题”做出更多贡献。我认为您对问题和解决方案、优点和缺点有很好的了解。

在我目前正在开发的应用程序中,我们通过同时拥有基本模型对象和基本控制器来利用其中的几种方法。为了最大程度地减少往返,我们将一些数据存储在会话中,并通过覆盖基本控制器中的 OnActionExecuted 重新填充到模型中,并从上下文中获取模型并在会话之外设置属性。

我当然也想听听任何出色的解决方案,但我认为这些只是要处理的权衡。

【讨论】:

以上是关于什么是 Asp.net MVC 页面构建的最佳实践,允许并行性和效率?的主要内容,如果未能解决你的问题,请参考以下文章

在 asp.net mvc 中返回错误的最佳实践是啥

asp.net mvc 站点的安全最佳实践?

asp.net mvc 开发人员的 javascript 最佳实践

在 Linux 上部署 ASP.NET MVC:最佳实践、工具和惊喜

ASP.NET MVC 4,EF5,模型中的唯一属性 - 最佳实践?

ASP.NET MVC 中密码重置的最佳实践