表现层及ASP.NET MVC介绍(二)

最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间。不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新。本文继续介绍表现层和Asp.net Mvc,我将在本篇讨论一些重要的设计问题和封装技巧。

是否需要将控制器分离为独立项目

  经常有人问我,是否有必要将控制器从Web项目中分离出来,下面谈一下我的认识,仅供你参考,不一定正确,请根据你自己的实际情况决定。

  控制器的作用是调用业务逻辑,将获得的结果传给视图显示。从根本上说,控制器只是起协调作用,它不应该自身完成复杂的业务逻辑。

  不过哪怕你将业务逻辑尽量放到了领域层,控制器上还是会有不少代码,比如查询条件的设置,事务控制等。在这种情况下,将控制器分离到一个独立的程序集,似乎说得过去。

  但是分离控制器代码有很多方法,比如引入应用层服务,应用层服务可以看成是控制器的延伸,控制器上大部分代码被转移到应用层服务中,控制器变成很薄的一层,这种情况下,分离控制器到独立项目,没有任何价值。

  将控制器分离到一个独立程序集后,你会发现查找控制器对应视图变得更加困难,哪怕Resharper可以帮你定位跳转。从目录结构上看,也不再遵循大家熟悉的约定,实际上降低了可维护性,增加了学习成本。

  最后的结论就是,如果你采用了应用层服务,就不要分离控制器。

是否需要将MVC项目拆分为多个

  对于表现层的模块化,MVC提供了Areas(区域)技术来支持较大项目的开发。每个区域包含独立的控制器和相关视图,为每个业务模块创建一个区域,对于一般项目的管理,应该都够用了。

  我在学习一些流行的应用开发框架时发现,有些应用开发框架将同一个Asp.Net Mvc项目,拆分为多个Mvc项目,以实现网站级别的模块化。其中一个是主Web项目,包含大部分WEB资源,比如图片、CSS、JS等。其它一些附属Web项目,包含其它业务模块,如果包含js文件,该文件的生成操作需要设置成“嵌入的资源”,包含的cshtml文件还需要用RazorGenerator插件手工生成类。最后主Web项目引用其它附属Web项目,从而整合为一个网站。

  这种架构的好处是允许复用业务UI模块,包括视图,同时方便团队协作,不同成员开发不同模块时干扰更小。

  但是这种架构也有很多问题,将JS设置成“嵌入的资源”,cshtml文件使用RazorGenerator生成类,会导致JS和页面调试部署都很不方便,调试时经常会碰上缓存问题。

  解决UI复用和协同工作的另一个办法是,多个网站独立开发部署,采用单点登录连接成一个系统。

  对表现层的模块化建议如下:普通项目采用单Mvc项目架构,通过Areas进行模块化, 更大项目采用多Mvc项目架构,单独开发部署,以单点登录方式集成,尽量不要将js、cshtml等页面元素嵌入程序集,会导致更难维护,仅在具有特殊需求时采用,比如插件式开发。

Asp.Net Mvc抽象封装技巧

  对于技术和业务逻辑,我们进行了复杂的分层和封装,那么对于页面本身,还要不要抽象?

  Html由标签组成,大量的Html标签,让你无法清晰的看出页面结构。当需要修改页面上某个区域时,如果页面很长很复杂,你需要在杂乱的Html标签中来回扫描,以定位目标,这与几百行的长方法类似。有经验的开发人员,总是以单一职责原则SRP为指导,通过提取方法和组合方法重构,保持方法的简短,同时获得清晰的代码结构。

  除了定位困难以外,未进行抽象的页面,将包含大量重复的Html,而冗余代码是可维护性的天敌。

  对于Asp.Net WebForm,抽象封装主要依赖母版页、服务器控件、用户控件等元素。

  母版页用于管理通用页面结构,帮助划分区域,它类似于抽象类,可以提供一部分具体实现,它将多个页面共享的部分抽取上来,并提供了内容占位符,占位符类似于虚方法或抽象方法,以方便具体页面填充内容。

  服务器控件用于封装能够高度复用的UI元素,提供增强功能,比如自定义文本框,一般的文本框只能输入文本,自定义文本框可以进行验证,甚至能进行权限控制等。

  用户控件是比服务器控件更轻量的封装方式,用户控件一般用于封装页面上的元素或区域。母版页的工作方式类似于继承,有经验的同学知道,继承的灵活性是比较低的,所以设计上流传一句话“多用委托,少用继承”,WebForm的“委托”就是用户控件,它同样可以切割页面,并且提供了更好的灵活性。

  如果很多页面都需要分成上中下三个区域,每个区域有一些固定的元素,这时候采用母版页就是合适的。但如果只有一个或几个页面需要这个结构,采用母版页将是大炮打蚊子,用户控件则是更好的选择。

  用户控件除了能够封装Html以外,还包含后置代码,可以处理逻辑。

  了解了WebForm的基础封装元素后,再来看Asp.Net Mvc提供了哪些元素,与Web Form的元素是如何对应的。

  首先看母版页,母版页的功能是提供布局结构,Mvc提供了布局页来支持类似功能。打开Mvc项目Views\Shared目录,会发现一个名为_Layout.cshtml的文件,这是系统定义的布局页,_Layout这个名称不是必须的,它能够起作用的原因是Views目录下的_ViewStart.cshtml设置指向它。不过没事不要乱改系统定义的文件名,这样会破坏约定,增加维护成本。

  _ViewStart.cshtml包含了Layout的设置,它的作用是为视图提供默认布局设置,值得一提的是,根目录Views下的_ViewStart.cshtml设置不能继承,Areas各模块必须添加自己的_ViewStart.cshtml。

  对于较复杂的系统,仅依靠一个_Layout.cshtml来抽象页面冗余是不够的,一般需要多层继承结构,_Layout.cshtml仅放置通用性很强的内容,比如js,css引用等,更具体的抽象可定义自己的布局页,继承_Layout.cshtml。

  Asp.Net Mvc不再提供服务器控件这种可视化元素,但相关的封装思想却从未间断。Mvc提供了HtmlHelper、UrlHelper、AjaxHelper等几个帮助类来封装相关操作,其中HtmlHelper包含表单元素的封装,是与WebForm服务器控件相对应的东西,我们封装控件主要从它下手。

  Mvc允许在视图中通过Html属性访问HtmlHelper,可以看到,所有控件都是通过扩展方法的方式添加上去的,这也给我们提供了一种思路。我在前文已经多次提到,使用扩展方法要很小心,特别是扩展系统的类,因为可能造成大面积污染。

  对于HtmlHelper,我一般仅扩展少量方法,首先是通用UI技术的封装,我会把需要在视图上用到的通用技术封装到@Html.X()方法中,比如导入一个Js文件,可以这样调用@Html.X().ImportJs(“xx”),当然现在导入Js一般用Bundle,本篇后续再介绍。

  其次是对特定UI技术的封装,比如Dwz,EasyUi,Ext等,也可能是其它组件,比如图表FusionCharts,ECharts等。对于每一个要用到的组件,都仅为其在HtmlHelper扩展一个方法,以EasyUi为例,你不能这样扩展,@Html.EasyUiTextBox(),@Html.EasyUiCombox(),这样会在HtmlHelper中扩展大量方法,导致查找其它方法变得困难,更好的方法是@Html.EasyUi().TextBox() ,@Html.EasyUi().Combox()。这里设计的核心是EasyUi方法返回一个接口,将EasyUi所有操作全部放到这个接口中。关于Mvc的控件封装,我会在后续EasyUi系列详细讲述。

  最后,需要在HtmlHelper中扩展的是业务UI组件,比如字典、省市区三级联动、人员选择等,我会把所有业务UI组件扩展到@Html.Ui()方法中,以方便开发某些业务模块时复用,比如字典@Html.Ui().Dic()。

  下面来看WebForm用户控件在Mvc中有哪些对应元素。

  如果页面中的某个区域很复杂,根据逻辑结构,将区域分解为更小的部分,每个部分放到一个用户控件中,从而得到清晰的结构。

  Mvc提供的@Html.Partial()方法允许将Html分离到部分视图中,并可以给这个部分视图传递一个实体,以进行数据绑定。

  不过部分视图的能力是有限的,你的主视图必须能够提供部分视图相关数据,这就要求主视图的实体携带更多的数据,这在很多时候都不太方便。打个比方,你的页面上需要显示一个下拉人员列表,列表的内容是用另一个表的数据填充的,如果采用部分视图来封装这个列表,你的主视图对应的实体就必须提供人员集合。

  Mvc提供了更为强大的@Html.Action()方法,Action也用于操作部分视图,但它能够独立的为视图提供数据。还是刚才的下拉人员列表,现在主视图的实体不再需要携带人员数据了,调用Action后,人员列表已加载完成。

  虽然Action更强大,但它需要设置Url信息,以确定这个功能由哪个控制器的方法提供,当某个Action操作用得非常频繁时,考虑将该操作扩展到HtmlHelper中,这样可以封装掉Url和参数信息,以简化调用。

  以上简单介绍了Mvc的一些封装元素,以供你编写出更清晰的UI代码。同时比较了Asp.net WebForm与Mvc的元素对应情况,你如果具有WebForm的基础,相信Mvc的封装也会很快上手。

  补充一点,虽然我用方法与Html长度类比,但不能认为Html的封装粒度越细越好,我曾经尝试过很细粒度的UI拆分,最终效果并不理想,合适的拆分粒度更好维护,这方面根据自己的习惯进行摸索。

Bundle介绍

  现在的系统Js和Css文件都很多,如果一个网站引用太多Js或css文件,对性能是有一定影响的,因为每个文件会发送一个请求。

  我以前的办法是使用AjaxMinifier工具手工压缩Js,再手工合并,Css也采用类似办法,后面使用了一个第三方的工具,也比较麻烦。

  Asp.Net为此提供了一个叫Bundle的打包压缩技术,它能够在运行时将js或css文件打包压缩,这正是我需要的。

  不过在使用过程中,发现它并不是想像中那么易用,问了一些人,也用起来有问题。还有一些人没碰到啥问题,但观察他的代码,实际上没有起作用,因为他没有设置启用优化的选项。

  使用Bundle有几点需要注意:

  1. 如果文件中的代码对路径很敏感,比如css中用了相对路径,你在配置Bundle时,虚拟路径就不能很随意,因为会破坏路径关系,导致失败。
  2. 如果没有在代码中设置BundleTable.EnableOptimizations = true,也没有在web.config进行相应配置,则打包压缩不会起作用,只是让你在引用文件时省点力。
  3. 已经压缩过的文件,比如jquery.min.js,不要用Bundle配置,使用常规方式引入,不然运行时可能出错。

Util最新代码示例更新

  除了之前的大量代码已重构外,主要更新了EasyUi的行内编辑方式。

结束语

本文简单介绍了Mvc相关的一些问题和技巧,有不同意见,欢迎交流。

下载地址:下载时请顺手推荐,以支持本人写作.

http://files.cnblogs.com/files/xiadao521/Applications.2015.3.16.1.rar

http://files.cnblogs.com/files/xiadao521/Framework.2015.3.16.1.rar

http://files.cnblogs.com/files/xiadao521/Data.2015.3.5.1.rar

注意:本人每次发布新版本时,会删除老版本

.Net应用程序框架交流QQ群: 386092459,欢迎有兴趣的朋友加入讨论。

.Net Easyui开发交流QQ群(本群仅限Easyui开发者,非Easyui开发者勿进):157809322

谢谢大家的持续关注,我的博客地址:http://www.cnblogs.com/xiadao521/

表现层及ASP.NET MVC介绍(二)的更多相关文章

  1. 表现层及ASP.NET MVC介绍(一)

    表现层及ASP.NET MVC介绍(一) 本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET ...

  2. 应用程序框架实战三十三:表现层及ASP.NET MVC介绍(二)

    最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间.不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新.本文继续介绍表现层和Asp.net Mvc,我将在本篇讨论一些 ...

  3. 应用程序框架实战三十:表现层及ASP.NET MVC介绍(一)

    本文将介绍表现层及ASP.NET MVC的一些要点,特别是ASP.NET MVC的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon G ...

  4. ASP.NET MVC 路由(二)

     ASP.NET MVC路由(二) 前言 在上一篇中,提及了Route.RouteCollection对象的一些信息,以及它们的结构所对应的关系.按照处理流程走下来还有遗留的疑问没有解决这个篇幅就来讲 ...

  5. ASP.NET MVC 视图(二)

    ASP.NET MVC 视图(二) 前言 上篇中对于视图引擎只是做了简单的演示,对于真正的理解视图引擎的工作过程可能还有点模糊,本篇将会对由MVC框架提供给我们的Razor视图引擎的整个执行过程做一个 ...

  6. 白话ASP.NET MVC之二:Controller激活系统的概览

    前文简介:我们抽象类路由规则的对象,RouteBase是路由对象的抽象基类,ASP.NET 的路由系统中有唯一一个从RouteBase继承的路由对象,那就是Route类型了.我们注册了路由对象Rout ...

  7. ASP.NET MVC进阶二

    一.数据验证 数据验证的步骤 在模型类中添加与验证相关的特性标记 在客户端导入与验证相关的js文件和css文件 使用与验证相关的Html辅助方法 在服务器端判断是否通过服务器端验证 常用的验证标记 R ...

  8. [转]深入ASP.NET MVC之二:路由模块如何工作

    本文转自:http://www.cnblogs.com/yinzixin/archive/2012/11/05/2754483.html 摘要: 上文分析了UrlRouting模块何时会被触发,本文重 ...

  9. ASP.NET MVC教程二:ASP.NET MVC应用程序结构详解

    在上一篇文章中,讲解了一些MVC的概念,并且创建了第一个ASP.NET MVC项目,这篇文章将讲解ASP.NET MVC程序中的代码解构,新创建的MVC应用程序解构如下图所示: 一.App_Data ...

随机推荐

  1. ORA-38760: This database instance failed to turn on flashback database 第三篇

    ORA-38760: This database instance failed to turn on flashback database  第三篇 第一篇 第二篇 问题现象:      在数据库a ...

  2. T-SQL使用案例——结果数据前面自动补0

    原文:T-SQL使用案例--结果数据前面自动补0 现象: 在开发的过程中,往往需要数字和字符串互转.在转换的过程中,可能需要把1编程00001,这样的格式.实现这种样子是有非常多的方法,本文主要提供一 ...

  3. Canvas 生成base64的PNG图片快照,So Amazing!!!

    function canvasSupport(){     return Modernizr.canvas;}function callCanvasApps(){  var canvasOne=doc ...

  4. Lazy<T>延迟初始化

    延迟初始化:Lazy<T> 1. 概述 我们创建某一个对象需要很大的消耗,而这个对象在运行过程中又不一定用到,为了避免每次运行都创建该对象,这时候延迟初始化(也叫延迟实例化)就出场了. 延 ...

  5. 编程算法 - 圆圈中最后剩下的数字(循环链表) 代码(C++)

    圆圈中最后剩下的数字(循环链表) 代码(C++) 本文地址: http://blog.csdn.net/caroline_wendy 题目: 0,1...,n-1这n个数字排成一个圆圈, 从数字0開始 ...

  6. Session中StateServer的使用方法

    最近项目中用到 Session的StateServer模式,我们知道sessionState有四种模式:off,inProc,StateServer,SqlServer. 而StateServer 是 ...

  7. FZU 2082 过路费(树链剖分)

    FZU 2082 过路费 题目链接 树链抛分改动边的模板题 代码: #include <cstdio> #include <cstring> #include <vect ...

  8. 【ORACLE】使用数据泵的生产环境impd,expdp数据迁移

    ********************************************** ** 原文: blog.csdn.net/clark_xu  徐长亮专栏 **************** ...

  9. 【智能家居篇】wifi网络访问原理(下一个)——联想Association

    转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 认证完毕后,下一步就是关联(Association). 工作站与基站进行关联.以便获得网络的全然訪问权. 关联属于一 ...

  10. 【C语言的日常实践(八)】弦

    串数据类型是一个重要的,但C有没有明确的语言字符串数据类型.头文件string.h它包括大多数字符串处理函数. 故,有操作的串.通常包括string.h头文件. 1.字符串的长度:对字符串进行的操作中 ...