ASP.NET MVC实例:使用Northwind和Entity框架


  本文标签:ASP.NET MVC实例

  作为asp.net 3.5 Extensions预览版组成部分的微软asp.net mvc框架发布了CTP版本  。自此,mvc框架就吸引了众多业界人士的眼球,并纷纷在博客以及各个开发者所关注的网站上进行了介绍  。

  微软CLR和.NET框架团队的成员Brad Abrams发布了一个非常优秀的实例,为开发者展示了如何有效地使用微软mvc框架中的某些最新工具  。该实例源于Scott Guthrie的mvc实例,Scott将这些内容放在他的博客上,演示了在最初形式下的mvc框架是如何进行工作的  。Scott的实例采用循序渐进的方式将其编写为四部分内容:

  asp.net mvc框架(第1部分)

  asp.net mvc框架(第2部分):URL路径选择

  asp.net mvc框架(第3部分):从控制器将ViewData传递到视图

  asp.net mvc框架(第4部分):处理表单编辑和提交场景

  mvc框架为开发者提供了足够的灵活性去选择视图与模型引擎以满足他们的需要  。在Scott的示例中,他使用了LINQ to SQL模型,但Brad决定使用Entity框架,并以Northwind数据库作为数据源  。

  开发者可以选择多种不同的模型提供者,例如:

  NHibernate

  LINQ to SQL

  Entity框架

  在将来,我们或许还可以看到其他的模型提供者:

  SubSonic

  LLBLGen Pro

  LightSpeed

  或者其他为人所知的

  Brad的方法是创建一个实例,引导开发者如何使用asp.net mvc Application and Test创建项目  。开发者需要安装下列内容:

  VS2008

  asp.net 3.5 Extensions

  ADO.NET Entity Framework Tools Dec 07 Preview

  Northwind sample database (Northwind.mdf)

  ASP.NET MVC实例指南

  该指南内容广泛,有很高的学习价值,从中可以获知mvc框架的功能以及如何将它们联系在一起  。使用mvc不同于以往所开发的Web Forms应用程序,甚至对于经验丰富的asp.net Web Form开发人员来说,也需要逐渐地习惯它  。

  入门

  一旦asp.net 3.5 Extensions安装完毕,就有几种项目类型可供选择,其中包括asp.net mvc Web Application以及asp.net mvc Web Application and Test  。asp.net mvc被设计为易于测试的框架,而Brad也使用了测试功能  。

  File/新建项目 - 选择asp.net mvc Web Application and Test

  File/新建项目 - 选择asp.net mvc Web Application and Test

  它会创建一个单独的解决方案,包括一个Web应用程序项目,并且,该项目可以用做单元测试  。它们都是预先生成的,包含了你需要创建的一些基础内容  。

  ASP.NET MVC实例:创建Routes

  mvc框架中的Routing(路径选择)是设计中非常值得关注的一项功能  。开发人员可以通过它判断应用程序如何查找页面  。在经典的asp.net应用程序中,一个页面例如home.aspx,总会有一个非常清晰的路径可以访问该页面,通常形如www.mywebsite.com/home.aspx  。与此比较,Routing为开发者提供了更多的灵活性  。

  asp.net mvc提供的其中一个非常强大的新特性是它能够定制访问应用程序的URLs  。显然,对于磁盘上的物理文件和用来访问页面功能的URL而言,URL路径选择特性隔离了两者之间的关联关系  。这对于搜索引擎的优化以及提高网站的通用性都是非常重要的  。例如,现在我们不需要访问这样的地址http://localhost/Products/ItemDetails.aspx?item=42,而是通过http://localhost/Products/CodFishOil进行访问,这样的URL更让人赏心悦目  。

  它的实现是在mvc应用程序的global.asax文件中创建一个路径表  。值得庆幸的是,模板中的默认内容对于应用程序而言已经足够了  。

  1. RouteTable.Routes.Add(new Route  
  2.  
  3. {      
  4.  
  5. Url = "[controller]/[action]/[id]",      
  6.  
  7. Defaults = new { action = "Index", id = (string)null },     
  8.  
  9.  RouteHandler = typeof(mvcRouteHandler)});  
  10.  

  这段代码给出了我们针对自己的站点所需要的URLs格式  。特别的,格式为

  http://localhost/Products/Details/CodFishOil

  的URL应该转换为ProductsController类(注意,我们为类名添加了“Controller”后缀,使得这些类能够区别于设计中的模型对象)  。接着,Action是Details类的方法,最后,传递给details方法的参数为CodFishOil  。

  当然,还可能有其它的格式,只需要在URL格式的字符串中修改正则表达式即可  。

  ASP.NET MVC实例:创建模型(Model)

  模型是大多数web应用程序的心脏,几乎所有的数据都存储在模型之中  。mvc框架允许开发者几乎没有限制地使用任意一种数据源,并能够轻松地在各种数据源之间进行切换  。

  模型表示你将要在应用程序中使用的数据  。在本例中,从模型开始应用程序的核心开发是一个不错的选择  。

  将Northwind.mdf文件复制到mvcApplication的App_Data文件夹中  。对于SqlServer而言,Northwind可能是最常用的示例数据库了  。你可以从官方地址 中下载它,如果您只需要原始文件的话也可以从这里获取   。

  如果您只需要原始文件的话也可以从这里获取

  接下来,我们需要基于northwind数据库创建LINQ模型,以便于进行操作  。你可以使用NHibernate 、LinqToSql 、Entity框架 ,或者其它的.NET ORM技术  。只要它的返回结果为.NET对象,asp.net mvc框架就能够对其进行操作  。在本例中,我使用了Entity框架  。

  右键单击Models目录,选择add new item

  右键单击Models目录,选择add new item 

  在对话框中,选择ADO.NET Entity Data Model  。

  在向导中,选择“Generate from Database”,然后赋予默认的“Northwnd”连接字符串  。

  对于演示而言,我们只需要使用Categories、Products和Supplier数据表,当然,你也可以扩展该演示以引入更多丰富的特性集  。但是目前除了这三者之外,不要选择数据库视图、存储过程和其他的数据表  。

  当你单击完成时,VS会创建一组.NET类

  当你单击完成时,VS会创建一组.NET类,这些类定制创建了访问数据库的相关内容  。我们还能够获得一个界面友好的设计器,以可视化方式展现数据之间的关系  。

  一个界面友好的设计器,以可视化方式展现数据之间的关系

  注意,赋予这些类的默认名仍然沿用了数据库的复数名词,但是在我们的OR映射中,它们表达的是单个的实例  。为了使得代码具有可读性,应该将所有的表名修改为准确的单数名词:Category、Product和Supplier  。至于Product的Navigation属性,也需要修改为单数形式,因为对于产品而言,只有一个类别(Category)和供应商(Suppler)  。

  右击设计视图,设置相关的属性

  接下来,我们需要修改命名空间使得代码更加准确直观……右击设计视图,设置相关的属性,例如将namespace设置为“NorthwindModels”,将Entity Container的名称设置为“NorthWindEntities”

  将Entity Container的名称设置为“NorthWindEntities”  

  虽然我们没有给出模型的完整示例,但给出的内容已经足以指导开发者完成剩余的内容……让我们跳转到下一个话题,来看一看控制器  。

  ASP.NET MVC实例:创建控制器

  控制器是我们应用程序的大脑  。我们可以将控制器想象为机场的空中交通控制器,指挥飞机的进出方向  。一方面,控制器负责获取数据,另一方面,它则负责将数据传递到视图  。

  右击Controller目录,选择“Add new Item”  。在对话框中找到mvc Controller,并确保赋予的名称是以Controller后缀结尾的  。在我们的例子中将会编写ProductsController类  。

  在我们的例子中将会编写ProductsController类  

  好的,我们现在从ProductsController.cs开始

  控制器的目的是为视图准备模型对象  。我们希望尽可能地将逻辑放到视图之外,因为它很难在视图中进行测试  。因此,在控制器中,我们会访问模型,并获得所有创建完毕的模型,这样,所有视图所要做的就是输出某些数据  。

  首先,我们需要访问数据库  。

  1. 添加正确的命名空间,包括Linq以及指向我们的OR映射的引用  。

  1. using System.Linq;  
  2.  
  3. using NorthwindModel;  

  2、接下来,我们需要创建NorthwindEntities容器类的实例  。几乎所有的action都会访问这个类  。

  1. public class ProductsController : Controller  
  2.  
  3. {     
  4.  
  5.  NorthwindEntities Northwind = new NorthwindEntities();  

  好的,现在我们需要创建第一个action:显示所有的类别  。记住,控制器的职责是为视图准备模型对象  。在定义一个新的action时,我喜欢首先编写一条注释,以提醒我访问这一功能的URL是什么  。

  接下来要做的事情就是从模型中访问Categories  。我将结果放到一个泛型集合类中(你可能需要添加System.Collections.Generic引用),然后将结果传递到名为“Categories”的视图中  。这是一个非常简单的例子,后面我们会在此处添加更加复杂的逻辑  。

  1. //URL: http://localhost/Products/Categories  
  2.  
  3. [ControllerAction]  
  4.  
  5. public void Categories()  
  6.  
  7. {     
  8.  
  9.  List categories = Northwind.Categories.ToList();      
  10.  
  11. RenderView("Categories", categories);  
  12.  
  13. }  
  14.  

  下一步,我们需要创建“Categories”视图  。

  ASP.NET MVC实例:创建视图

  对于视图而言,mvc框架给了开发者和模型差不多的灵活性  。开发人员可以选择一组视图引擎并对它们进行切换  。

  右击Views文件夹,添加新的目录“Products”  。这使得我们可以清晰地组织我们的视图  。右击Views/Products文件夹,然后添加一个新项mvc View Content Page  。我们会充分地利用Master Page,它是在默认的项目中已生成的,可以使得界面看起来更加友好  。

  右击Views/Products文件夹,然后添加一个新项mvc View Content Page  。  

  将该页面命名为Categories.aspx  。视图的名称非常重要,它必须与之前提及的RenderView方法的第一个参数相匹配  。

  默认项目会将Master Page放到Views/Shared/Site.Master中

  默认项目会将Master Page放到Views/Shared/Site.Master中

  ViewData是我们要从控制器中传递的内容,为了得到对它的强类型访问,我们需要告知视图页面它所期待的类型  。这可以通过打开codebehind文件(Categories.aspx.cs),修改继承的类型来完成:

  1. public partial class Categories : ViewPage  
  2.  
  3. {  
  4.  
  5. }  
  6.  

  修改为:

  1. public partial class Categories : ViewPage<  List >   
  2.  
  3. {  
  4.  
  5. }  
  6.  

  接着你就可以编写清晰、简单、可设计的HTML了  。注意,我在这里将所有从ViewData返回的元素项进行了一次循环,然后将它们作为链表传出  。我使用了mvc的辅助方法Html.ActionLink,为包含了对应产品ID的List action创建了URL  。

  1. < % foreach (var category in ViewData) { %>      
  2.  
  3.  < %= Html.ActionLink(category.CategoryName, new { action="List", id=category.CategoryName }) %>  
  4.  
  5. < % } %>  
  6.  

  ASP.NET MVC实例:浏览产品

  好的,一切准备妥当,可以运行了  。

  按下F5,导航条上就会出现我们刚才编写的控制器action:http://localhost:64701/products/Categories

  导航条上就会出现我们刚才编写的控制器action:  

  点击任何一个链接都会出现一个错误,因为我们还没有编写List action  。这是我们接下来所要做的  。

  说句题外话,如果你像我这样习惯在开发的aspx页面上使用“View in Browser”,你可能会看到这个错误  。

  若要重现此错误,请右击Categories.aspx,然后选择View in browser  。

  请右击Categories.aspx,然后选择View in browser

  你会获得一个错误  。为什么?是的,请务必谨记在mvc模型中,所有的执行都要经过控制器,视图自身是不能运行的  。未来的工具会对此进行改进,但至少在现在,可以对default.aspx使用F5或者通过“run in browser”进行操作  。当然应该首先确保你已经编译了解决方案  。

  List Action视图现在,让我们回到之前省略的内容:添加List action  。在这里我们需要的是在给定的Category中查找所有的产品  。首先,我需要从模型中获取所有产品,然后我必须确保Category的引用已经被加载  。Entity框架在默认情况下提供了一个显式的加载模型  。因此,你必须明确地加载你所需要的所有表  。最后,我们再呈现视图  。

  1. //example URL:http://localhost:64701/products/List/Confections  
  2.  
  3. [ControllerAction]  
  4.  
  5. public void List(string id)  
  6.  
  7. {     
  8.  
  9.  List products = Northwind.GetProductsByCategory(id);      
  10.  
  11. //prepare the view by explicitly loading the categories        
  12.  
  13. products.FindAll(p => p.Category == null).ForEach(p => p.CategoryReference.Load());      
  14.  
  15. RenderView("ListingByCategory", products);  
  16.  
  17. }  
  18.  

  注意,我调用了NorthwindDataContext类的一个自定义方法  。我个人倾向于将所有的数据访问逻辑封装到这个类中  。若要定义该方法,可以右击Model,通过add new item选择CodeFile,并命名为NorthwindDataContext.cs,然后给出如下的实现  。

  1. using System;  
  2.  
  3. using System.Collections.Generic;  
  4.  
  5. using System.Linq;  
  6.  
  7. namespace NorthwindModel  
  8.  
  9. {     
  10.  
  11.  public partial class NorthwindEntities     
  12.  
  13.  {     
  14.  
  15.  }  
  16.  
  17. }  
  18.  

  现在,你可以很容易地为该类添加数据访问方法了,例如我们之前使用的GetProductsByCategory()方法  。

  1. public List GetProductsByCategory(string category)  
  2.  
  3. {      
  4.  
  5. return Products.Where(p => p.Category.CategoryName == category).ToList();  
  6.  
  7. }  
  8.  

  下一步,我们需要添加ListingByCategory视图  。遵循前面介绍的相同步骤,我们在Views/Products/目录下添加ListingByCategory.aspx页面  。

  这一次,我们应该让ViewData成为List类型

  1. public partial class ListingByCategory : ViewPage  
  2.  
  3.  {  
  4.  
  5. }  
  6.  

  接下来实现视图,我们只是对视图的数据进行了循环,并以正确的格式输出  。

  1. < %--Print out the catagory name--%>   
  2.  
  3.  < % foreach (var product in ViewData) { %>    
  4.  
  5.   < % if (product.Category.CategoryName != null) {  %>     
  6.  
  7.       < %=product.Category.CategoryName  %>      
  8.  
  9.     < % break; %>   < %} //end if %>< %}//end foreach %>  
  10.  
  11. < % foreach (var product in ViewData) { %>    
  12.  
  13.   < img alt="< %=product.ProductName %>" src="/Content/Images/< %=product.ProductID%>.jpg" />    
  14.  
  15.   < %=product.ProductName %>      
  16.  
  17.  Price: < %=String.Format("{0:C2}", product.UnitPrice)%>   
  18.  
  19. < % } %>  
  20.  

  一旦你在实例项目中添加了/Content/Images目录,就会获得如下页面:

  一旦你在实例项目中添加了/Content/Images目录,就会获得如下页面  

  Brad的实例是用C#编写的,因此Julie Lerman选择创建了和Brad相似的例子,她使用了VB.NET和AdventureWorksLT数据库 ,并重点关注了更多高效的Entity框架查询  。Julie指出了她的实现与Brad的重大不同之处  。

  我的EDM(译者注:指实体数据模型)创建自AdventureWorksLT数据库  。

  在AW(译者注:AdventureWorksLT的简写)中,SalesOrderHeaders和Customer的关系与Northwind中Products和Category的关系相同  。因此,在他使用Products的地方,我使用SalesOrderHeaders;在他使用Categories的地方,我使用Customers  。

  若要轻易地获取数据并将其传给视图,则其中一个关键是我们需要传递“一个”对象(而且不是匿名类型)到视图  。然而,对于Order列表(每一个都具有Customer的名字)和Details列表(每一个都具有从Order和Customer中获得的数据)而言,我们真正需要的是一个对象图  。