使用 Northwind 和 Entity 框架的 ASP.NET MVC 实例发布

  • Robert Bazinet
  • 张逸

2008 年 2 月 23 日

话题:.NET语言 & 开发

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

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

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

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

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

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

指南

该指南内容广泛,有很高的学习价值,从中可以获知 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

image

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

创建 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 文件中创建一个路径表。值得庆幸的是,模板中的默认内容对于应用程序而言已经足够了。

RouteTable.Routes.Add(new Route

{

Url = "[controller]/[action]/[id]",

Defaults = new { action = "Index", id = (string)null },

RouteHandler = typeof(MvcRouteHandler)

});

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

http://localhost/Products/Details/CodFishOil

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

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

创建模型(Model)

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

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

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

image

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

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

image

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

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

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

image

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

image

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

image

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

image

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

创建控制器

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

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

image

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

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

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

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

using System.Linq;

using NorthwindModel;

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

public class ProductsController : Controller

{

NorthwindEntities Northwind = new NorthwindEntities();

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

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

//URL: http://localhost/Products/Categories

[ControllerAction]

public void Categories()

{

List categories = Northwind.Categories.ToList();

RenderView("Categories", categories);

}

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

创建视图

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

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

image

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

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

image

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

public partial class Categories : ViewPage

{

}

修改为:
public partial class Categories : ViewPage< List > 

{

}

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

<% foreach (var category in ViewData) { %>

<%= Html.ActionLink(category.CategoryName, new { action="List", id=category.CategoryName }) %>

<% } %>

浏览产品

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

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

image

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

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

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

image

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

List Action 视图

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

//example URL:http://localhost:64701/products/List/Confections

[ControllerAction]

public void List(string id)

{

List products = Northwind.GetProductsByCategory(id);

//prepare the view by explicitly loading the categories



products.FindAll(p => p.Category == null).ForEach(p => p.CategoryReference.Load());

RenderView("ListingByCategory", products);



}

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

using System;

using System.Collections.Generic;

using System.Linq;

namespace NorthwindModel



{

public partial class NorthwindEntities

{

}

}

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

public List GetProductsByCategory(string category)

{

return Products.Where(p => p.Category.CategoryName == category).ToList();

}

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

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

public partial class ListingByCategory : ViewPage

{

}

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

<%--Print out the catagory name--%>  

<% foreach (var product in ViewData) { %>

<% if (product.Category.CategoryName != null) { %>

<%=product.Category.CategoryName %>

<% break; %>

<%} //end if %>

<%}//end foreach %>

<% foreach (var product in ViewData) { %>

<img alt="<%=product.ProductName %>" src="/Content/Images/<%=product.ProductID%>.jpg" />

<%=product.ProductName %>

Price: <%=String.Format("{0:C2}", product.UnitPrice)%>

<% } %>

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

image

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 中获得的数据)而言,我们真正需要的是一个对象图。

那些希望下载完整的应用程序,而不希望循序渐进地了解实现过程的开发人员可以从Brad 的博客中下载能够直接运行的实例

查看英文原文:ASP.NET MVC Example with Northwind and Entity Framework Published
.NET语言 & 开发