【转载】MVC索引页
序言
做为设计模式的王者,MVC在众多领域都成为良好的模型的代名词,从前在ASP.NET下我们只能依靠Monorail来实现ASP.NET下无控件的MVC,但是现在ASP.NET 下的MVC已经成为现实。
本文只想让大家更直观地认知ASP.NET MVC,如果语言有所不当,还望大家海涵。当然,如果文中有所纰漏还希望大家及时指出,我也好做及时的修改。
在下本着对初学者负责的态度来书写本系列中的各篇文章,但期间的恒心与毅力相信过来的人更加明白,所以如果书写有误希望大家谅解。
ASP.NET MVC 正式版相关信息
ASP.NET MVC曾经是ASP.NET 3.5 Extensions Preview 的一个部分.如今历经五个Preview一个Beta两个RC版本终于走向我们。
最新版本:ASP.NET MVC正式版1.0
- 安装包: ASP.NET MVC RTM release
- 源代码及特性程序集:ASP.NET MVC v1.0 Source
- 文档:ASP.NET MVC MSDN 文档
- 博客园专题:ASP.NET MVC专题
文章编写约定
- 本文的前提环境为.NET 3.5,但笔者会尽力写在.NET2.0 SP1下兼容的程序
- 文本中所使用的IDE都为Visual Studio 2008(中文) 语言基本为C#不过为了方便大家理解 ,也可能会有一些Visual Basic
- 笔者计算机操作系统为Windows 2003 std/Windows Vista/Windows 7
- 其它约定笔者将会后续补充
交互与问答
ASP.NET MVC 系列文章
以下文章属于ASP.NET MVC 1.0 正式版
- ASP.NET MVC雕虫小技 1-2
- ASP.NET MVC 重点教程一周年版 第十一回 母版页、用户自定义控件及文件上传
- ASP.NET MVC 重点教程一周年版 第十回 请求Controller
- ASP.NET MVC 重点教程一周年版 第九回 HtmlHelper
- ASP.NET MVC 重点教程一周年版 第八回 Helper之演化
- 用ASP.NET MVC自己管理自己的View:ASP.NET MVC File Management
- 使用ASP.NET MVC Futures 中的异步Action
- ASP.NET MVC 重点教程一周年版 第七回 UrlHelper
- ASP.NET MVC 重点教程一周年版 第六回 过滤器Filter
- ASP.NET MVC 重点教程一周年版 第五回 ActionResult的其它返回值
- ASP.NET MVC 重点教程一周年版 第四回 向View传值
- ASP.NET MVC 重点教程一周年版 第三回 Controller与View
- ASP.NET MVC 重点教程一周年版 第二回 UrlRouting
- ASP.NET MVC 重点教程一周年版 第一回 安装,并使ASP.NET MVC页面运行起来
以下文章属于ASP.NET MVC 1.0 RC
以下文章属于ASP.NET MVC 1.0 Beta
- Asp.net Mvc Enum 扩展
- DynamicData for Asp.net Mvc留言本实例 上篇 准备工作及显示文章列表
- DynamicData for Asp.net Mvc留言本实例 中篇 新建.删除.数据验证
- DynamicData for Asp.net Mvc留言本实例 下篇 更新
以下文章属于Asp.net Mvc CodePlex Preview 5
- Asp.net Mvc开发体会点滴 一
- Asp.net Mvc Codeplex Preview 5 源代码及MVCContrib4pv5发布
- Asp.net Mvc Codeplex Preview 5 第三篇 实现Action参数传递繁杂类型
- Asp.net Mvc Codeplex Preview 5 第二篇 Controller&Filter的新特性
- Asp.net Mvc Codeplex Preview 5 新特性 一 Helper
- System.Web.Routing入门及进阶 下篇
- System.Web.Routing入门及进阶 上篇
- System.Web.Routing 的说明文档
- Microsoft.Web.Mvc Assembly 说明
以下文章属于Asp.net Mvc CodePlex Preview 4
- NVelocity View Engine with Asp.net Mvc
- System.Web.Abstractions中的装饰者模式,及其在Asp.net Mvc中的应用
- Asp.net MVC各个类的说明(Preview 4)
- 使用MvcContrib的FormHelper
- Asp.net Mvc Framework在.net 2.0/IIS6下运行,程序示例
- Asp.net Mvc Framework可以在Controller中使用的Url.Action方法
- Asp.net Mvc Framework在.net 2.0/IIS6下运行,补全
- Asp.net MVC Preview 4 中自定义Jquery的HtmlHelper扩展
- Asp.net MVC Preview 4 中使用RenderComponent
- Asp.net Mvc Pv4中使用AjaxHelper
- ASP.NET MVC CodePlex Preview 4 Installer + Source + Changed
以下文章是属于Asp.net MVC preview 3
以下文章是属于Asp.net MVC preview 2
- Asp.net Mvc Framework 一 (安装并建立示例程序)
- Asp.net Mvc Framework 二 (URL Routing初解)
- Asp.net Mvc Framework 三 (Controller与View)
- Asp.net Mvc Framework 四 (在.net2.0下运行)
- Asp.net Mvc Framework 五 (向View传值以及Redirect)
- Asp.net Mvc Framework 六 (更多的View传值及显示方式)
- Asp.net Mvc Framework 七 (Filter及其执行顺序)
- Asp.net Mvc Framework 八 (Helper)
- Asp.net Mvc Framework 九 (View与Controller交互)
- Asp.net Mvc Framework 十(测试方法及Filter的示例)
- Asp.net Mvc Framework 十一 (自定义Helper在MVC中的使用)
- Asp.net Mvc Framework 十二 Castle扩展
- Asp.net MVC P2 中无法正确获取 CheckBox值的bug的解决方案
- Asp.net Mvc中MVCContrib中无法使用Castle的发解决方案
我的一些ASP.NET MVC的开源项目
- ASP.NET MVC File Management(文件管理)
- CHMVCMS(内容管理系统)
- CHOJ#(在线编译评判)
- ASP.NET MVC Ajax(Ajax操作)
- CHSNS#(SNS开源项目)
- Infancy (A Forum Application)(ASP.NET MVC论坛)
- WebAsk(类似百度知道的问答系统)
相关站点
ASP.NET技术
===================
被称为一个漂亮的Asp.net MVC应用,从代码角度来看,我认为得满足这三点:
1. 使用依赖注入框架。
2. 不要直接依赖Cache, HttpContext等。
3. View中不要条件逻辑。
当然不只是这三点,还有很多。我个人拿它们出来,是认为这些很重要但是经常被忽视。
对于第1点,优点在于松耦合,可测试性很好。如果在Controller里面想要使用某些Service,要么new出来,要么用单例的形式,如UserService.Instance,这样想对Controller写单元测试都不容易,它和这些Service耦合太紧密,无法将这些Service替换成Stub实现。因此,松耦合是必须的。要实现这个功能,必须让依赖注入框架来创建Controller,才有可能注入依赖并组装对象。MVC里面有一个ControllerFactory的东西,可以使用来达到这个目的。
a. 写一个类,继承自DefaultControllerFactory,例如 UnityControllerFactory : DefaultControllerFactory
b. 覆盖方法GetControllerInstance,使用依赖注入框架来创建Conroller
c. 修改Global.asax.cs, 在Application_Start内注册使用自己的ControllerFactory,
ControllerBuilder.Current.SetControllerFactory(new UnityControllerFactory())
d. 对Controller进行构造函数注入,例如:
public class UserController : Controller
{
public UserController(IUserService, userService, IMessageService messageService)
…
}
从现在开始,所有的Controller都是通过依赖注入框架来创建的,新增的Service就在依赖注入框架里面注册,Controller要使用哪些Service就往构造函数里加,反正框架会注入进来。
第2点, 类如HttpContext.Current.Session[“xxx”] = …, HttpContext.Current.Cache.Insert…此样的代码充斥在各个角落。有问题吗?还是老问题,可测试性,可替换性。
像HttpContext这种东西,几乎是不能Mock或者Stub的,只要代码中使用了HttpContext,可以说它就没法做单元测试。为什么很多人都会这么用呢,一个原因是图方便图简单,二是没有写单元测试。要用Session直接用就好了,封装一下再用多麻烦啊,这就是图简单。
解法很简单,对HttpContext进行封装,例如ISessionProvider, ICacheProvider,然后通过依赖注入框架,注入到Controller中去,这样的结果是代码可测试性高,而且想改变Cache机制也很方便。
意识不够。什么意识?让代码松耦合的意识。使用静态方法,静态变量,直接new依赖的对象,这些都是松耦合的反例。我们写代码的时候要规避它们。即使我们不用TDD(极端一点来说,现在不写单元测试),脑子里也要清楚记得,面向对象的法则、松耦合的一些原则等等,然后反应到代码上去。
(推荐)asp.net mvc强大的分页控件MvcPager
继我的【Ajax服务端框架】完成后, 也花了些时间学习了一Asp.net MVC,感觉我的Ajax框架也能玩MVC开发, 于是乎,在又加了点功能后,现在也能像Asp.net MVC那样写aspx和ascx了。
先来点代码来看看,具体的页面呈现效果请参考: 通用数据访问层及Ajax服务端框架的综合示例,展示与下载
<%@ Page Title="商品管理" Language="C#" MasterPageFile="~/MasterPage.master" Inherits="FishWebLib.Mvc.MyPageView<ProductsPageData, ProductsModelLoader>" %> <asp:Content ID="Content1" ContentPlaceHolderID="head" Runat="Server"> <script type="text/javascript" src="/js/MyPage/Products.js"></script> </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" Runat="Server"> <table cellpadding="4" border="0"> <tr><td>当前分类</td><td> <select id="ddlCurrentCategory" onchange="<%= UiHelper.DropDownListAutoRedir("ddlCurrentCategory")%>" combobox="not-editable" style="width:300px;"> <%= Model.Categories.ToHtmlOptions("categoryId", false, "searchWord", "page")%> </select> </td><td> <a id="btnCreateItem" href="#" class="easyui-linkbutton" iconCls="icon-add">添加商品</a> </td></tr> </table> <div style="height: 10px"></div> <table class="GridView" cellspacing="0" cellpadding="4" border="0" style="border-collapse:collapse;"> <tr align="left"> <th style="width:20px;"> </th> <th style="width:550px;">商品名称</th> <th style="width:120px;">单位</th> <th style="width:120px;">单价</th> <th style="width:50px;">数量</th> </tr> <% foreach( Product product in Model.Products ) { %> <tr> <td><a href="/AjaxProduct.Delete.cs?id=<%= product.ProductID %>&returnUrl=<%= this.CurrentRequestRawUrl %>" title="删除" class="easyui-linkbutton" plain="true"> <img src="/Images/delete.gif" alt="删除" /></a> </td> <td style="white-space:nowrap;"> <a href="#" rowId="<%= product.ProductID %>" class="easyui-linkbutton" plain="true" iconCls="icon-open" > <%= product.ProductName.HtmlEncode()%></a> </td><td> <span name="Unit"><%= product.Unit.HtmlEncode() %></span> </td><td> <span name="UnitPrice"><%= product.UnitPrice.ToText() %></span> </td><td> <input type="text" pid="<%= product.ProductID %>" value="<%= product.Quantity %>" class="quantityTextbox" /> </td> </tr> <% } %> <%= Model.PagingInfo.PaginationBar(5)%> </table> <input type="hidden" id="hfCurrentCategoryId" value="<%= Model.CurrentCategoryId %>" /> <div id="divProductInfo" title="商品" style="display: none"><div style="padding: 8px"> <fish:ProductInfo ID="ProductInfo1" runat="server" ModelDataName="Model.ProductInfoViewData" /> </div></div> </asp:Content>
再来个ascx
<%@ Control Language="C#" Inherits="FishWebLib.Mvc.MyUserControlView<OrderListViewData>" %> <table class="GridView" cellspacing="0" cellpadding="4" border="0" style="border-collapse:collapse; width: 99%"> <tr align="left"> <th style="width:100px;">订单编号</th> <th style="width:160px;">时间</th> <th style="width:300px;">客户</th> <th style="width:100px;">订单金额</th> <th style="width:60px;">已处理</th> </tr> <% foreach( var item in Model.List ) { %> <tr> <td> <a href="#" OrderNo="<%= item.OrderID %>" class="easyui-linkbutton" plain="true" iconCls="icon-open"> <%= item.OrderNo %></a> </td> <td><%= string.Format("{0:yyyy-MM-dd HH:mm:ss}", item.OrderDate) %></td> <td> <a href="#" Customer='<%= item.ValidCustomerId %>' class="easyui-linkbutton" plain="true" iconCls="icon-open"> <%= item.CustomerName.HtmlEncode() %></a> </td> <td><%= item.SumMoney.ToText() %></td> <td> <%= item.Finished.ToCheckBox(null, "chk_Finished", true) %> </td> </tr> <% } %> <%= Model.PagingInfo.PaginationBar(5) %> </table>
与Asp.net MVC的aspx,ascx一样:没有与之对应的CodeFile
再来看看"商品管理"页面加载数据的C#代码吧。注意类型的名称与方法的签名,它们在上面Page指令中有引用。
public class ProductsModelLoader { public static ProductsPageData LoadModel() { ProductsPageData result = new ProductsPageData(); result = new ProductsPageData(); result.Categories = BllFactory.GetCategoryBLL().GetList(); if( result.Categories.Count == 0 ) { //HttpContext.Current.Response.Redirect("/Categories.aspx", true); // 不建议使用这个方法,不利于测试 FishWebLib.HttpContextHelper.RequireRedirect("/Categories.aspx"); return null; } // 获取当前用户选择的商品分类ID result.CurrentCategoryId = FishWebLib.FishUrlHelper.GetIntegerFromQueryString("categoryId", 0); if( result.CurrentCategoryId == 0 ) result.CurrentCategoryId = result.Categories[0].CategoryID; result.ProductInfoViewData = new ProductInfoViewData(result.Categories, new Product { CategoryID = result.CurrentCategoryId }); result.PagingInfo.PageIndex = result.PagingInfo.GetPageIndex(); result.PagingInfo.PageSize = AppHelper.DefaultPageSize; // 加载商品列表,并显示 result.Products = BllFactory.GetProductBLL().GetProductByCategoryId(result.CurrentCategoryId, result.PagingInfo); return result; } } public class ProductsPageData { public PagingInfo PagingInfo; public List<Category> Categories; public int CurrentCategoryId; public List<Product> Products; public ProductInfoViewData ProductInfoViewData { get; set; } public ProductsPageData() { this.PagingInfo = new PagingInfo(); } }
上面那个ascx是供JS调用的,C#的实现代码如下:(注意是如何执行用户控件的)
/// <summary> /// 搜索订单,并以HTML代码的形式返回给客户端 /// </summary> /// <returns></returns> public static string Search() { // 从查询字符串中读取客户端发过的日期范围。 QueryDateRange range = QueryDateRange.GetDateRangeFromQueryString("StartDate", "EndDate"); OrderListViewData data = new OrderListViewData(); data.PagingInfo.PageIndex = data.PagingInfo.GetPageIndex(); data.PagingInfo.PageSize = AppHelper.DefaultPageSize; // 搜索数据库 data.List = BllFactory.GetOrderBLL().Search(range, data.PagingInfo); // 执行用户控件,并传递所需的呈现数据。 return FishWebLib.Ajax.UcExecutor.Execute("~/Controls/OrderList.ascx", data); } public class OrderListViewData { public List<OrderItem> List; public PagingInfo PagingInfo = new PagingInfo(); }
与Asp.net MVC框架一样,页面数据的加载和呈现彻底地分开了。
JS调用上面那个ascx的代码如下:
function btnQuery_Click(){ var dateRange = GetDateRange("txtStartDate", "txtEndDate"); if( dateRange == null ) return false; var url = '/AjaxOrder.Search.cs?' + $.param({StartDate: dateRange.StartDate, EndDate: dateRange.EndDate}); ShowPickerPage(url, 'divResultList_inner', ShowResultSuccess); return false; }
代码回顾
学过Asp.net MVC的朋友应该能发现,前面示例代码中的 aspx, ascx的写法,与Asp.net MVC的写法是100%的兼容的。 但我肯定没有使用Asp.net MVC,您可以下载示例代码去看。
当初学习Asp.net MVC是因为有些人把Asp.net MVC说得太过于神话了,好像用Asp.net做开发,非它不可。但是,学了之后才发现, 确实,这个东西有很多优点,但没那么神奇。我认它最出色的地方还是在于它的思想:MVC,这种思想能够让我们在开发时, 尽量去分离一些可分离的东西,比如:呈现与获取数据的过程,从而可以在一个地方只关注一件事情,也就是它所提倡的:分离关注点。
正如前面所说,我认为Asp.net MVC的优点在于它的设计体现了MVC的思想。但让人不解的是:微软在实现时,不知为何, 非要把MVC搞得与WebForms差别巨大。这种区别尤其是在加入了Route和一堆Html扩展之后。 对于Route和一堆Html扩展,我对它们没多大的兴趣,因为它们于MVC无关,对于代码的维护和性能根本没什么改善。 所以也不打算在这个山寨版本上采用,反而认为现在的代码更容易理解了。
至于在Ajax方面的实现,我的框架也完全是符合MVC思想的。 而且在生成HTML片段时,仍然可以使用MVC的思想:先准备数据,然后交给ascx来呈现。
我的MVC框架与Asp.net MVC的不同之处:
1. 简单了很多,抛弃了许多与MVC无关的东西。因此,学习成本更低。
2. Controller少了很多限制,可以在单独的类库中实现。为了方便安全检查,建议放在一个命名空间中,或者取个前缀或后缀,这也符合:约定胜于配置。
3. 由于没有Route,页面是由Asp.net框架所调用的,而不是先经过Controller,但页面的数据加载是由一个辅助的类来完成的, 这个类一定要实现一个名为“LoadModel”的方法(签名不限,框架会识别),同样,也是采用约定胜于配置的原则。
‘
----------------------------------------------
在我们建立Action时,有个原则,就是最好和页面名称相同,如果一个名为Index的Action,
那它的页面就应该是Index.aspx/cshtml,
如果你非要建立一个Index1.aspx,那路由是无法找到的,
当然,如果你想让路由找到,就用return View("Index1")来代替return View()就可以了
-------------------------------------------------------
当前标签: MVC
邮箱:steven9801@163.com
QQ: 48039387