转自http://firechun.blog.163.com/blog/static/318045222011029105328664/
迄今为止,我们只是把“模拟数据”从控制器传递到视图模板,现在,我们要挂上真实的数据库了。在教程中,我们演示了如何使用免费的SQL Server Express做为我们的数据库引擎,这些代码也同样适用于完整版的SQL Server。
首先在项目中添加App_Data目录存放SQL Server Express数据库文件。App_Data是ASP.NET的特定目录,它已经为数据库访问设定了合适的访问权限。
添加数据库
右键单击项目,选择“添加->添加ASP.NET文件夹->App_Data”
现在可以添加数据库文件了。为教程使用的数据库MvcMusicStore.mdf已经创建好了,它包含在可供下载的项目包的MvcMusicStore-Assets/Data目录下,下载地址:http://mvcmusicstore.codeplex.com
注 意: MvcMusicStore-Assets/Data 目录中还包括一个 T-SQL 脚本 (MvcMusicStore-Create.sql) 可以在一个SQL Server实例中创建 MvcMusicStore 数据库,假如你不能使用SQL Server Express的话。
(以下是如何添加现有项到项目中的说明和截图,此处略过,只需要将下载的MvcMusicStore.mdf做为现有项添加到App_Data目录下就行了)
看看数据库的关系图:
你可以看到描述音乐的Album、Genre和Artist类,还有涉及到仓库管理的Cart(购物车)、Order(订单)以及OrderDetails(订单明细)
使用实体框架连接数据库
数据库已经添加到项目中,我们可以编写代码查询和更新数据库,这里我们使用.NET 4中的实体框架(EF)来实现。EF是一个灵活的对象关系映射(ORM)数据API,允许开发人员使用面向对象的方式查询和更新数据库中的数据。
EF4支持的开发模式称为代码优先。代码优先允许你编写简单的类创建模型对象,甚至允许你从类中动态创建数据库,要使用代码优先,你必须安装EFCodeFirst 库。
注意: 代码优先可以从你的模型类中创建数据库。在本教程中,我们使用现有的数据库,其中预先加载了由完整的类别和艺术家信息组成的音乐专辑,要获得从模型产生数据的示例,参看Scott Hanselman’s 介绍的 ASP.NET MVC 教程: http://www.asp.net/mvc/tutorials/getting-started-with-mvc-part1.
用NuGet安装EFCodeFirst库
在这一节,我们将使用NuGet Package Manager(由ASP.NET MVC 3自动安装)把EFCodeFirst添加到MvcMusicStore项目中,NuGet Package Manager随ASP.NET MVC 3一起安装。
(没有安装MVC 3,当然也就没NuGet Package Manager,下面插入一段如何安装NuGet Package Manager的过程)
在VS2010中选择“工具->扩展管理器”
搜索完成后,找到NuGet Package Manager,下载安装并重启VS2010,下面的步骤和原文一样了。
从工具菜单中,选择 Library Package Manager\Add Library Package Reference(如果出错,重启VS2010,重新再来)
Add Library Package Reference对话框显示
选择online
这里会有几百个包,我们只对EFCodeFirst感兴趣,在搜索框中输入“EFCode”,选择“EFCode”包并点击安装。
包安装完成之后,点击“Close”按纽。安装程序已经下载EFCodeFirst库并把它添加到MvcMusicStore项目中,EFCodeFirst库在EntityFrameword.dll文件中。
在Web.config文件中创建连接字符串
在网站的Web.config添加一行以便让实体框架知道如何连接字符串,在项目根目录中找到并双击Web.config文件:
<connectionStrings>
<add name="MusicStoreEntities"
connectionString="data source=.\SQLEXPRESS;
Integrated Security=SSPI;
AttachDBFilename=|DataDirectory|\MvcMusicStore.mdf;
User Instance=true"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
添加Context类
在Models文件夹上单击右键添加一个新类:MusicStoreEntities.cs
这个类用来呈现实体框架上下文(Context),为我们处理创建、读取、更新和删除操作,代码如下所示:
using System.Data.Entity;
namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
}
}
就这样,不需要其它配置、特殊的接口等等。派生自DbContext的MusicStoreEntities类为我们各种处理数据库操作。了解了如何和数据库挂接,我们再给模型类增加一些属性以便应用数据库中追加的信息。
更新模型类
更新Album类,如下:
namespace MvcMusicStore.Models
{
public class Album
{
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public Genre Genre { get; set; }
}
}
下面接着更新Genre类
using System.Collections.Generic;
namespace MvcMusicStore.Models
{
public partial class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Album> Albums { get; set; }
}
}
查询数据库
现在可以更新StoreController了,用从数据库中查询到的信息替换“模拟数据”。首先在StoreController类中声明一个字段用来保存MusicStoreEntities的实例:
public class StoreController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
在Store的Index方法中使用LINQ查询表达式
MusicStoreEntities类由实体框架维护,并公开了数据库中每个表的属性集合。我们可以使用.NET中很酷的LINQ(语言集成查询)针对这些集合编写强类型查询表达式,它可以对数据库执行这些代码并返回对象,并且编程更加容易。
现在把之前在StoreController中Index动作方法中硬编码生成的字符串数据(类别名称)更新为从数据库中检索,我们编写一个如下所示的LINK查询表达式从数据库中检索每个类别的Name属性。
public ActionResult Index()
{
var genres = storeDb.Genres.ToList();
return View(genres);
}
对之前的Index.aspx视图不需要做任何改变,只不过我们现在得到的是数据库中的数据。
运行项目并访问/Store URL,我们看到了数据库中的所有类别。
在Store的Broswe、Details和Index方法中使用LINQ扩展方法
在指向 /Store/Browse?genre=[some-genre] 的动作方法中,我们按名称查询类别,因为不可能有两个相同名称的类别,我们只有一个返回结果,所以可以使用LINQ查询的.Single()方法来返回Genre对象。如:
var example = storeDB.Genres.Single(g => g.Name == “Disco”);
Single方法的参数是一个Lambda表达式,用它来确定和我们定义的名称相符的单个Genre对象。上面的例子中,我们读取了一个名称为“Disco”的Genre对象。
检 索到Genre对象时,我们可以利用实体框架的特点来标示出该对象的相关实体,这种实体框架的特点被称为Query Result Shaping(查询结果整形?没有找到正式的中文叫法),这可以在我们从数据库检索我们所需要的信息时降低访问数据库的次数。我们想为检索到的类别预先 获取相关的唱片,因此更新我们的查询,让它包括Genres.Include("Albums"),以表明我们希望获得相关的唱片。因为在单次的数据库请 求中同时查询唱片和类别,性能会更好。
更新Browse方法如下:
public ActionResult Browse(string genre)
{
// Retrieve Genre and its Associated Albums from database
var genreModel = storeDB.Genres.Include("Albums")
.Single(g => g.Name == genre);
return View(genreModel);
}
更新Storer的Browse视图以显示每个类别下的唱片,打开视图模板( /Views/Store/Browse.aspx)添加唱片列表代码:
<h2>
Browsing Genre:<%:Model.Name %></h2>
<ul>
<% foreach (var album in Model.Albums)
{ %>
<li><%:album.Title%></li>
<% } %>
</ul>
运行程序并浏览/Store/Browse?genre=Jazz显示我们从数据库获得的数据,将会显示我们指定类别下的所有唱片。
对/Store/Details/[id] URL做相同的修改,使用从数据库中查询到的和Id值相符的数据来替换“模拟数据”。
public ActionResult Details(int id)
{
var album = storeDB.Albums.Find(id);
return View(album);
}
运行应用程序,浏览/Store/Details/5,显示我们从数据库中获得的结果。
现在Store Details页面被设定为按Id显示唱片,更新Browse视图,让它链接到Details视图。我们使用Html.ActionLink()方法,但并不完全象之前从Store Index链接到Store Browse那样,完整代码如下:
<h2>
Browsing Genre:<%:Model.Name %></h2>
<ul>
<% foreach (var album in Model.Albums)
{ %>
<li><%:Html.ActionLink(album.Title, "Details", new { id = album.AlbumId }) %></li>
<% } %>
</ul>
现在我们可以Store页面浏览到Genre页面,可以列出所在唱片,并且点击唱片时可以查看唱片的详细信息。