现在,我们已经能够把静态数据从控制器传入我们的视图模板中了。接下来,我们将要使用数据库中的数据。在本教程中,我们使用SQL Server Express来作为我们的数据库引擎。
4.1 使用Entity Framework code-first连接数据库
在本教程中,我们使用.NET 4中的Entity Framework(EF)来访问数据库以及对数据库中的数据进行增删查改操作。EF是一个非常灵活的对象关系映射(ORM:object relational mapping)数据库API,它使得开发者可以用一种面向对象的方式在数据库中进行增删查改操作。
Entity Framework 4支持一种被称为code-first的开发范例。code-first允许你用书写简单类的方式来创建模型对象(也称POCO对象),甚至可以直接通过模型类来创建数据库。为了要使用code-first,首先需要安装EFCodeFirst类库。
4.2 使用NuGet来安装EFCodeFirst
这里我们打算使用NuGet包管理器(安装ASP.NET MVC3时会同时自动被安装)来在本应用程序中添加EFCodeFirst类库。
点击“工具”菜单下的“Library Package Manager”子菜单下的“Add Library Package Reference”菜单选项,如图4-1所示。

图4-1 使用NuGet包管理器
点击“Add Library Package Reference”菜单选项后,将会弹出一个对话框,标题为“Add Library Package Reference”,如图4-2所示。

图4-2 “Add Library Package Reference”对话框
默认状态下,左边的“All”选项处于选择状态。因为还没有安装任何包,所以右边面板中显示“找不到任何项”,如图4-3所示。

图4-3 默认状态下显示“找不到任何项”
点击左边面板中的“online”选项,NuGet包管理器将会在服务器上检索所有当前能够获取的包,如图4-4所示。

图4-4 NuGet包管理器正在检索包信息
服务器上有几百个当前能够获取的包,现在我们只关注EFCodeFirst包。在右上角的搜索输入框中输入“EFCode”。在检索结果中,选择EFCodeFirst包,并且点击Install按钮安装包,如图4-5所示。

图4-5 选择EFCodeFirst包并安装
点击了install按钮后,会弹出一个接受许可证窗口,如图4-6所示,在这个窗口中必须要点击“I Accept”按钮,接受许可证条款,安装才能继续进行。

图4-6 接受许可证窗口
安装完毕后,点击close按钮。我们的MvcBookStore工程中会自动加载EntityFramework程序集,其中包含了EFCodeFirst类库,如图4-7所示。

图4-7 安装完毕后EntityFramework程序集被自动加载
4.3 在web.config文件中创建一个连接字符串
接下来我们将在应用程序的设置文件中追加一个连接字符串,供Entity Framework连接数据库时所用。鼠标双击解决方案资源管理器中工程根目录下的Web.config文件,如图4-8所示。

图4-8 解决方案资源管理器中工程根目录下的Web.config文件
在该文件的底部追加一个<connectionStrings>区段,代码如下所示。
<configuration>
<connectionStrings>
<add name="ApplicationServices"
connectionString="data source=.\SQLEXPRESS;Integrated
Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;
User Instance=true"
providerName="System.Data.SqlClient" />
<add name="BookStoreEntities"
connectionString=" Data Source=.\SQLEXPRESS;
Initial Catalog=BookStore;Persist Security Info=True;
User ID=aaa;Password=aaaaaaa "
providerName="System.Data.SqlClient" />
</connectionStrings>
4.4 追加一个上下文类
鼠标右击Models文件夹,追加一个新类,名字为BookStoreEntities.cs,如图4-9所示。

图4-9 追加BookStoreEntities.cs上下文类
这个类代表了Entity Framework数据库上下文,为我们处理数据的增删查改操作,追加该类中代码如下所示。
using System.Data.Entity;
namespace MvcBookStore.Models
{
public class BookStoreEntities : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Genre> Genres { get; set; }
}
}
这样就足够了,不需其他任何设置,譬如专有接口等等。通过对DbContext基础类的扩展,我们的BookStoreEntities类可以替我们实现有关数据库的操作。接下来,让我们在我们的模型类中追加一些属性,以便获取数据库中的一些附加信息。
4.5 修改我们的模型类
将Book类中的代码修改为如下所示的代码。
namespace MvcBookStore.Models
{
public class Book
{
public int Id { get; set; }
public int GenreId { get; set; }
public int AuthorId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public Genre Genre { get; set; }
}
}
接下来,让我们修改Genre类中的代码如下所示。
using System.Collections.Generic;
namespace MvcBookStore.Models
{
public partial class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Book> Books { get; set; }
}
}
4.6 查询数据库
接下来,让我们更新我们的StoreController控制器类,使其可以到我们的数据库中进行数据的查询。首先,我们创建一个MusicStoreEntities类的实例,并将其命名为storeDB。
public class StoreController : Controller
{
BookStoreEntities storeDB = new BookStoreEntities();
4.7 在Index方法中使用LINQ查询
Entity Framework维护BookStoreEntities类,并且为数据库中每张数据表向外公布一个集合属性。我们可以使用.NET中的LINQ查询功能来针对这些属性书写强类型的查询表达式,这些查询表达式将执行数据库中的查询并且返回一些易于我们编程时使用的对象。
修改StoreController控制器类中Index这个action方法,使其可以到数据表中查询所有的书籍种类名称。这里我们使用如下代码中所示的LINQ查询来获取数据表中所有种类(Genre)的Name(名称)属性。
public ActionResult Index()
{
var genres = storeDB.Genres.ToList();
return View(genres);
}
接下来需要修改Views文件夹下的Store文件夹中的Index.cshtml文件夹中的一处地方,将“genre.ID”修改为“GenreId”,代码如下所示。
<li>@Html.ActionLink(genre.Name,"Browse", new {id=genre.GenreId})</li>
我们不需要对之前我们的视图模板中的代码做出任何修改,该视图模板可以直接显示从数据表中取出的书籍种类信息。
重新运行应用程序,并访问“/Store”这个URL地址,该页面中目前没有任何书籍种类信息被显示,如图4-10所示。

图4-10 数据表中尚未添加种类信息时的页面显示
打开你在web.config中指定连接的SQL Server Express的SQL Management Studio,该服务器中已被追加了一个BookStore数据库,该数据库中被自动追加了一张Book数据表与一张Genre数据表,打开Genre数据表,输入如表4-1中所示的数据。
表4-1 在Genre表中追加的数据
GenreId(自增长ID) | Name(种类名称) | Description(描述) |
1 | 小说 | 包括玄幻、穿越、青春校园、魔幻、都市、历史、科幻、商战、军事等等 |
2 | 文学 | 包括古典文学、现代文学、诗歌、散文等等 |
3 | 社会科学 | 包括励志、两性关系、管理、股票、军事、政治、历史、法律、哲学、宗教等等 |
4 | 艺术 | 包括绘画、书法、影视、动画、戏曲、摄影、雕塑、音乐、舞蹈等等 |
5 | 教材 | 包括小学课外参考书、初中课外参考书、高中课外参考书等等 |
6 | 生活 | 包括养生、家具、装修、旅游、育儿、饮食等等 |
7 | 科学 | 包括计算机类、机械类、建筑类、医学类、交通类、电子类等等 |
重新运行应用程序,并访问“/Store”这个URL地址,该页面中显示所有书籍种类信息,如图4-11所示。

图4-11 页面中显示所有书籍种类信息
4.8 在Browse方法与Details方法中使用LINQ查询
通过输入“/Store/Browse/[ID]”这个URL地址,我们应该可以通过一个ID来查找一种书籍种类。我们只想要一条查询结果,因为书店中不可能有两个相同的书籍种类,所以我们采用如下所示的方法来查询Genre对象。
Single方法使用了一个Lambda表达式来作为参数,该表达式查询一个Genre对象,对象的GenreId为我们所指定的ID值。在此处代码中,我们查询一个GenreId值为1的Genre对象。
var example = storeDB.Genres.Single(g => g.GenreId == 1);
此处我们将利用Entity Framework中的一个特性,该特性允许我们获取Genre对象时同时可以获取与之相关联的其他实体信息,该特性被称为Query Result Shaping,它可以减少当我们获取所有需要使用到的数据对象信息时访问数据库的次数。我们可以为种类预装载书籍信息,所以修改查询表达式,使用Genres.Include(“Books”)来标示我们同时还需要获取相关联的书籍信息。这种做法是十分高效的,因为它在一次数据库请求中同时获取了种类信息和与之相关联的书籍信息。
Browse方法中最终被修改代码如下所示。
public ActionResult Browse(int id)
{
//从数据库中获取书籍种类信息和与之相关联的书籍信息
var genreModel = storeDB.Genres.Include("Books").Single(g => g.GenreId ==
id);
return View(genreModel);
}
现在我们修改书籍种类信息展示页面,展示每一个书籍种类中的所有书籍。打开该视图模板(Views文件夹下的Store文件夹中的Browse.cshtml文件),修改代码如下所示。
@model MvcBookStore.Models.Genre
@{
ViewBag.Title = "挑选书籍";
}
<h2>书籍种类: @Model.Name</h2>
<ul>
@foreach (var book in Model.Books)
{
<li>
@book.Title
</li>
}
</ul>
重新运行应用程序,并访问“/Store/Browse/1”这个URL地址,该页面中目前没有任何书籍信息被显示,如图4-12所示。

图4-12数据表中尚未添加书籍信息时的页面显示
打开你在web.config中指定连接的SQL Server Express的SQL Management Studio,打开Book数据表,输入如表4-2中所示的数据。
表4-2 在Book表中追加的数据
Id(自增长字段) | GenreId | AuthorId | Title | Price |
1 | 1 | 1 | 1988:我想和这个世界谈谈 | 25 |
2 | 1 | 2 | 风中的费洛蒙 | 28 |
3 | 1 | 3 | 海边的卡夫卡 | 27 |
4 | 1 | 4 | 大方 | 25 |
5 | 1 | 5 | 骑誓•蛊骑士的灵印 | 20 |
6 | 1 | 1 | 独唱团 | 16 |
7 | 1 | 6 | 苏小姐的婚事:六六的十二则"婚事"启示录 | 25 |
8 | 1 | 7 | 不能承受的生命之轻 | 29 |
9 | 1 | 8 | 追风筝的人 | 25 |
10 | 1 | 9 | 圣经密码 | 28 |
重新运行应用程序,并访问“/Store/Browse/1”这个URL地址,该页面中显示所有小说类的书籍信息,如图4-13所示。

图4-13 页面中显示所有小说类的书籍信息
接下来我们修改Details这个action方法中的代码,当用户输入“/Store/Details/[ID]”这个URL地址时,能够到数据库中查询书籍信息,并显示在页面上。代码如下所示。
public ActionResult Details(int id)
{
var book = storeDB.Books.Find(id);
return View(book);
}
重新运行应用程序,并访问“/Store/Details/1”这个URL地址,页面中显示出了对应的书籍信息,如图4-14所示。

图4-14 页面中显示单本小说信息
最后,让我们修改书籍种类信息展示页面,使其能够链接到单本小说信息展示页面。我们使用Html.ActionLink方法,使用方法类似于前文所述的从书籍种类列表页面与书籍种类信息展示页面之间的链接,代码如下所示。
@model MvcBookStore.Models.Genre
@{
ViewBag.Title = "挑选书籍";
}
<h2>书籍种类: @Model.Name</h2>
<ul>
@foreach (var book in Model.Books)
{
<li>
@Html.ActionLink(book.Title,"Details",new { id = book.Id })
</li>
}
</ul>
现在,当我们点击书籍种类信息里的某本小说时,页面就能跳转到单本小说信息展示页面并显示该书籍的信息了。