最近的更新速度越来越慢,主要是项目上比较忙,封装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的一些抽象和封装技巧,如果你对MVC还不了解,可以参考<ASP.NET MVC4 高级编程>,作者Jon G ...

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

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

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

    表现层及ASP.NET MVC介绍(二) 最近的更新速度越来越慢,主要是项目上比较忙,封装EasyUi也要花很多时间.不过大家请放心,本系列不会半途夭折,并且代码干货也会持续更新.本文继续介绍表现层和 ...

  4. 【WePY小程序框架实战三】-组件传值

    [WePY小程序框架实战一]-创建项目 [WePY小程序框架实战二]-页面结构 父子组件传值 静态传值 静态传值为父组件向子组件传递常量数据,因此只能传递String字符串类型. 父组件 (paren ...

  5. 应用程序框架实战三十八:项目示例VS解决方案的创建(一)

    进行项目开发的第一步,是创建出适合自己团队习惯的VS解决方案,虽然我已经提供了项目示例,但毕竟是我创建的,你直接使用可能并不合适,另外你如果尝试模仿重新创建该示例,中间可能碰到各种障碍,特别是项目间的 ...

  6. 应用程序框架实战三十七:Util最新代码更新说明

    离上一篇又过去了一个月,时间比较紧,后续估计会更紧,所以这次将放出更多公共操作类及配套的CodeSmith模板,本篇将简要介绍新放出的重要功能,供有兴趣的同学参考. 重要更新 这一次对两个VS解决方案 ...

  7. 应用程序框架实战三十六:CRUD实战演练介绍

    从本篇开始,本系列将进入实战演练阶段. 前面主要介绍了一些应用程序框架的概念和基类,本来想把所有概念介绍完,再把框架内部实现都讲完了,再进入实战,这样可以让初学者基础牢靠.不过我的精力很有限,文章进度 ...

  8. 应用程序框架实战三十四:数据传输对象(DTO)介绍及各类型实体比较

    本文将介绍DDD分层架构中广泛使用的数据传输对象Dto,并且与领域实体Entity,查询实体QueryObject,视图实体ViewModel等几种实体进行比较. 领域实体为何不能一统江湖? 当你阅读 ...

  9. 【WePY小程序框架实战四】-使用async&await异步请求数据

    [WePY小程序框架实战一]-创建项目 [WePY小程序框架实战二]-页面结构 [WePY小程序框架实战三]-组件传值 async await 是对promise的近一步优化,既解决了promise链 ...

随机推荐

  1. Ubuntu 14.04安装Cinnamon桌面环境

    2014年05月26日 薄荷开源网,mintos,网如其名,自然最关注的还是 Linux Mint 这个 DistroWatch 排名第一的 Linux 发行版.奈何此前的 Linux Mint 17 ...

  2. Go 语言的基本数据类型

    Go 语言的基本数据类型 0)变量声明 var 变量名字 类型 = 表达式 例: 其中“类型”或“= 表达式”两个部分可以省略其中的一个. 1)根据初始化表达式来推导类型信息 2)默认值初始化为0. ...

  3. Linux 环境变量 设置 etc profile

    一.Linux的变量种类 按变量的生存周期来划分,Linux变量可分为两类: 1.永久的:需要修改配置文件,变量永久生效. 2.临时的:使用export命令声明即可,变量在关闭shell时失效. 二. ...

  4. java发送邮件..转

    使用Java应用程序发送E-mail十分简单,但是首先你应该在你的机器上安装JavaMail API 和Java Activation Framework (JAF) . 你可以在 JavaMail ...

  5. 【转】Android Studio-1.2版本设置教程

    如果重新安装Android Studio的话要重新配置风格选项啥的,这篇是个很好的教程,原文链接:http://blog.csdn.net/skykingf/article/details/45485 ...

  6. Unity3D 导航网格自动寻路(Navigation Mesh)

    NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路..导航时,只需要给导 ...

  7. 微软StockTrader应用程序

    这是一个采用 .NET Enterprise Application Server 技术的端到端示例应用程序.应用程序代码可以从 这里 下载. 代码中演示了WCF服务和移动开发,包括用Xamarin ...

  8. C#实现DNS解析服务和智能DNS服务

    C#实现DNS解析服务有一个开源项目ARSoft.Tools.Net, ARSoft.Tools.Net是一个非常强大的开源DNS控件库,包含.Net SPF validation, SenderID ...

  9. NodeJs 开发微信公众号(二)测试环境部署

    由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...

  10. UI控件(UIToolbar)

    @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; _toolbar = [[UIToolbar all ...