学习ASP.NET已经很久了,一直以来觉得ASP.NET 的MVC模式和WinForm的三层架构差不多。每次老师讲到MVC的时候都是略过不讲的,所以仅仅凭自己的一点点浅薄的认识,就以为MVC就是三层架构。这种想法不仅仅是错的,更是一种不负责任,不求甚解,人云亦云的不端正的科学态度。作为一名IT工作者,更应该以一种科学严谨的态度对待知识和学问,更应该学会独立的思考,而不是随波逐流,人云亦云。
这些天花了一些时间来把ASP.NET MVC 模式了解了下,觉得自己之前的认识实在是大错特错了。三层架构的模式在WinFom程序里边运用的比较多,现在的开发已经不拘于具体的模式了,甚至有N层架构的出现,我们要做的是如何在不拘于具体模式的前提下,提高软件系统的性能、开发的高效性、代码的复用性。MVC模式,ASP.NET MVC Framework是微软官方提供的MVC模式编写ASP.NET Web应用程序的一个框架,MVC(Model-View-Controller)用于表示一种软件架构模式.它把软件系统分为三个基本部分:模型(Model),视图(View)和控制器(Controller).,和三层架构的相似之处在于分层的思想,MVC模式很好的实现了UI、业务逻辑、数据库访问的分层,在mvc中这三部分分别被称之为视图(View)
控制器(Controller)和模型(Model),下面就这三部分的内容一一介绍。
与ASP.NET项目不同,ASP.NET MVC 项目建成之后项目文件目录如下:
(基本的文件夹的 用途):
- App_Data :这个目录跟我们一般的ASP.NET website是一样的,用于存放数据。
- Content :这个目录是建议用来存放一下资源文件的。例如CSS、JS、图片等等。当然你不愿意的话,完全可以不放到这里来。
- Controllers :这个目录是建议将Controller类都放到这里来,方便管理。Controller类的命名必须以Controller结尾,例如一个名为Home的Controller则要命名为HomeController。
- Models :这个目录是建议用来存放你的业务实体、数据访问层代码的类的。当然,更好的做法我觉得应该是将Models独立为一个类库。
- Views :在默认情况下,所有的 view文件都必须放到这个目录下来,每一个Controller对应一个子目录,而且子目录的命名必须以Controller的命名一样。例如,HomeController的view就应该放到Home子目录中。我们见到Views目录下还有一个Shared的子目录,这个子目录是用于存放一些共享的view的,例如Error.aspx和Site.Master。Controller在Views\ControllerNmae 中找不到指定的view的时候,会到Shared中去寻找
下面进入具体的学习:
一、路由。
谈到MVC模式就不得不谈到路由,和传统的WebForm模式不同,WebFrom采用的是事件驱动模式,MVC采用的是路由驱动模式。ASP.NET路由模块负责将即将到来的浏览器请求映射到特定的MVC控制器动作,然后再由控制器进行调配,采取一些列的动作最后再将相应的视图呈现给大家。
在一个route中,通过在大括号中放一个占位符来定义( { and } )。当解析URL的时候,符号"/"和"."被作为一个定义符来解析,而定义符之间的值则匹配到占位符中。route定义中不在大括号中的信息则作为常量值。下面是一些示例URL:
Valid route definitions |
Examples of matching URL |
|
{controller}/{action}/{id} |
/Products/show/beverages |
|
{table}/Details.aspx |
/Products/Details.aspx |
|
blog/{action}/{entry} |
/blog/show/123 |
|
{reporttype}/{year}/{month}/{day} |
/sales/2008/1/5 |
通常,我们在Global.asax文件中的Application_Start事件中添加routes,这确保routes在程序启动的时候就可用,而且也允许在你进行单元测试的时候直接调用该方法。
下面的示例是Global.asax中的代码,演示了添加一个包含两个URL参数action和categoryName的Route对象:
public class MvcApplication : System.Web.HttpApplication { public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute( "Default", // 路由名称 "{controller}/{action}/{id}", // 带有参数的 URL new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值 );
}
protected void Application_Start() { AreaRegistration.RegisterAllAreas();
//在程序启动的时候注册我们前面定义的Route规则
RegisterRoutes(RouteTable.Routes); } }
这些均为默认的设置
如果有特殊的需要也可以自定义配置路由规则,如下所示:(自定义路由)
routes.MapRoute( "Category", // Route name "Category/{Detailid}", // URL with parameters new{ controller ="Category", action ="Details" } // Parameter defaults
);
自定义路由添加到路由表中的路由顺序非常重要。新自定义Category路由在现有的Default路由前面。如果你将这个顺序颠倒过来,那么Default路由将总是被调用,而不是自定义路由。
当一个MVC应用程序首次运行时,会调用Application_Start()方法。这个方法随后调用了RegisterRoutes()方法。RegisterRoutes()方法创建了路由表。
默认的路由表包含了一个路由(名叫Default)。Default路由将URL的第一部分映射到控制器名,URL的第二部分映射到控制器动作,第三个部分映射到一个叫做id的参数。
假设你在浏览器的地址栏输入了下面的URL:
/Home/Index/1
默认的路由将这个URL映射为下面的参数:
Controller = Home
Action = Index
id = 1
当你请求URL /Home/Index/1时,将会执行下面的代码:
HomeController.Index(1)
Default路由包含了所有三个参数的默认值。如果你不提供控制器,那么控制器参数默认值为Home。如果你不提供动作,动作参数默认为值Index。最后,如果你不提供id,id参数默认为空字符串
此时对应的控制器中的方法为HomeController.cs
public ActionResult Index() { // imgDBEntity = new ImageDBEntity(); //ViewData["Message"] = "欢迎使用 ASP.NET MVC!"; //ViewData["Images"] = imgDBEntity.Imgs.ToList(); //return View(reporsitory.ListAll());
return View(); }
另外在有些地方需要建立相应的路由约束,比如上面新添的路由Category,控制器公布的行动Details(int DetaillID)需要传入参数DetailID
public class ProductController : Controller { public ActionResult Details(int DetaillID) { return View(); } } 代码会匹配:
/Category/12
/Category/333
等URL
但是如果有非数字字符出现的URL
/Category/abc
则会提示报错访问的URL不存在
另外比如你的Admin页不希望被访问到,这时就需要阻止某个路径匹配,这时候可以自定义一个路由,可以通过实现IRouteConstraint接口来实现一个自定义路由,它只描述了一个方法:
bool Match( HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection )
这个方法返回一个布尔值。如果返回了false,与约束相关联的路由将不会匹配浏览器请求
实现接口
public class LocalhostConstraint : IRouteConstraint { public bool Match ( HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection ) { return httpContext.Request.IsLocal; }
同时在Globle.cs中做如下配置
routes.MapRoute( "Admin", "Admin/{action}", new {controller="Admin"}, new {isLocal=new LocalhostConstraint()} );
这样如果不是本机访问就无法访问到Admin页。
二、视图(View)
与ASP.NET不同的是它本身并没有具体呈现的某个页,它仅仅是将要到达浏览器的URL请求映射为一个控制器动作,然后控制器动作进行一些列的动作将视图返回
例如:HomeController.cs中的Index动作
public ActionResult Index() { ViewData["Message"] = "欢迎使用 ASP.NET MVC!"; return View(); }
这个动作返回的是Index视图。我们所创建的大多动作都返回一个视图,但是动作是可以返回任何类型的动作结果的。
Index()动作包含了下面一行代码:
return View();
这行的代码返回了一个视图,该视图在服务器上的路径必须和下面的路径一样:
ViewsHomeIndex.aspx
视图的路径由控制器和控制器动作的名称推断得出。
与此同时你也可以显式地指明视图。下面一行代码返回了一个视图名为“Add”:
return View("Add");
当执行这行代码时,将会从下面的路径返回一个视图:
ViewsHomeAdd.aspx
接下来讲创建视图,视图的创建很简单,在View文件夹下建立和控制器同名的文件夹,比如本例的“Home”文件夹,然后在Home文件夹下点击右键添加,然后选择视图就可以了。
一般我们来向视图添加内容的时候会用到<%%>,在试图页,我们可以通过<%%>脚本执行一条或多条语句,例如:
<% Response.Write(DateTime.Now);%>
因为我们可能要频繁的使用Response.Wirte()这个方法,所以微软对此进行的封装<%= %>与<% Response.Write();%>是等效的。
除此之外,我们还可以通过HtmlHelper来生成视图内容,
例如:
<div > <%using (Html.BeginForm()) { %> <label for="firstName">First Name:</label> <%=Html.TextBox("firstName")%> <br /> <label for="lastName">Last Name:</label> <%=Html.TextBox("lastName")%> <%} %> <br /> <input type="submit" value="Submit" /> </div>
生成如下效果:
在MVC3框架下可以直接这样用
@using (Html.BeginForm()) { @Html.ValidationSummary() <P>Your Name:@Html.TextBoxFor(x=>x.Name)</P> <p>Your Email:@Html.TextBoxFor(x=>x.Email)</p> <p>Your Phone:@Html.TextBoxFor(x=>x.Phone)</p> } <input type="submit" value="Submit RSVP" />
在MVC中,构建数据是controller的工作,将数据作为HTML的呈现是View的工作,很明显这里有一个将数据从Controller传递到View的过程。可以通过ViewData属性来传递视图,
HomeController.cs中
public ActionResult Index() { imgDBEntity = new ImageDBEntity(); ViewData["Message"] = "欢迎使用 ASP.NET MVC!"; return View(); }
在Index.aspx页
<h2><%= Html.Encode(ViewData["Message"]) %></h2>
这样就可以显示数据。
与此同时,其中的一种方式就是通过ViewBag,ViewBag是Controller基类的一个成员,它是一个动态的对象,我们可以给它赋予任意的属性值,并在View中呈现
public ViewResult Index() { ViewBag.Hello ="Olive"; return View(); }
在Index.aspx
<h2><%= Html.Encode(ViewBag.Hello) %></h2>
三、控制器(Controller)
在MVC模式中,控制器就相当于一个分发器,通过响应相应的行为,然后和Model层进行交互,提取请求所需的内容,进而返回一个视图,或者另一个行为。
控制器的创建可以在项目的Controller文件下,点击右键添加Controller文件即可。
例如HomeController.cs
[HandleError] public class HomeController : Controller { public ActionResult Index() { ViewData["Message"] = "欢迎使用 ASP.NET MVC!"; return View(); } [AcceptVerbs(HttpVerbs.Post)] public ActionResult Index(FormCollection formCollection) { var move=new Movie(); move.ID = 3; TryUpdateModel(move, new string[] { "firstName", "lastName" }, formCollection.ToValueProvider()); MoviesDataContext mv = new MoviesDataContext(); mv.Movie.InsertOnSubmit(move); return View("Index");
} public ActionResult Add() { return View(); }
我们上边所说的动作就是控制器的一个方法,当你在浏览器输入一个Url时,相应的控制器会执行相应的动作,也就是相应的方法。例如:
在本例中,Index()方法在HomeController类上被调用。一个控制器动作必须是控制器类的一个公共方法。控制器动作来使用的方法不能够重载。另外,控制器动作不能为静态方法。
控制器动作执行完以后需要返回给浏览器一些东西,即我们所说的控制动作结果
ASP.NET MVC框架支持六种标准类型的动作结果:
- ViewResult – 代表HTML及标记。
- EmptyResult – 代表无结果。
- RedirectResult – 代表重定向到一个新的URL。
- RedirectToRouteResult – 代表重定向到一个新的控制器动作。
- JsonResult – 代表一个JSON(Javascript Object Notation)结果,它可以用于AJAX应用程序。
- ContentResult – 代表着文本结果。
所有这些动作结果都继承自ActionResult基类。在大多数情况下,控制器动作 ViewResult。ContentResult动作结果很特别。你可以使用ContentResult动作结果来将动作结果作为纯文本返回。
四、模型(Model)
MVC模型包含了所有MVC视图或者MVC控制器没有包含的应用程序逻辑,一个MVC模型包含了所有的应用程序业务和数据访问逻辑,所以模型往往被称作是MVC的核心。
构建数据访问类的方法有很多,而且ASP.NET与任何数据访问都可以很好的融合,这里主要讲Liinq to Sql与MS数据库交互。
首先在App_Data文件夹下创建数据库,然后再创建一个文件表 Imgs,具体如下:
主键自增。
下边接着创建Linq to Sql 类,在Model文件夹下右键添加,然后选中左侧列表里的Data项,选中里边的Linq to Sql 将会立即出现“对象关系设计器(Object Relational Designer)”。此时,你可以将数据库表从“服务器资源管理器(Server Explorer)”窗口中拖曳到“对象关系设计器”中,来创建代表着特定数据库表的LINQ to SQL类。这里我们添加Imgs 表。如图:
public ActionResult Index(){
var dataContext = new ImgsDataContext(); var imgs = from m in dataContext.Imgs select m; return View(imgs.ToList()); } 此时同时也要修改Index页的内容,进行数据的显示:
代码如下:Index.aspx
<div> <%foreach (Imgs im in (IEnumerable)ViewData.Model)//要注意此出需要将ViewData.Model进行强制转换下否则无法进行佛reach遍历 {%> <%=im.Name %> <%=im.Title %> <img src=" <%=im.Url %> " />
<div>
同时还要在页面的开始部位引入<%@ Import Namespace="MVC1.Models" %>命名空间 这样就可以很好的显示数据了
效果如图:
后记:这篇算是对这几天的学习的一个总结和反思吧,希望能对有兴趣的朋友有一点帮助。如果有说的不对的地方还请拍砖!