ASP.NET MVC 教程 - 使用实体框架创建模型类(C#)
在这篇教程里,你会学习如何在ASP.NET MVC里使用微软实体框架(Microsoft Entity Framework)。你会学习怎样使用实体向导来创建一个ADO.NET 实体数据模型。通过这个教程的课程,我们会建立一个Web应用来说明如何通过Entity框架来选择、插入、更新和删除数据。
使用实体框架创建模型类(C#)
这篇教程的目的是向你解释这你创建一个ASP.NET MVC应用的时候,你可以怎样使用微软实体框架来创建数据接入类。这篇课程假定你之前没有微软实体框架的知识。在这篇教程的末尾,你会明白怎样使用实体框架来选择、插入、更新和删除数据库记录。
微软实体框架(Microsoft Entity Framework)是一个对象关系映射工具(Object Relational Mapping ,O/RM)工具。它可以让你从一个数据库自动地生成数据接入层。实体框架免去了你手工创建数据接入层的乏味工作。
为了向你说明怎么在ASP.NET MVC里使用微软实体框架的,我们将会创建一个简单的应用程序。我们将创建一个Movie数据应用。它可以让你显示和编辑数据库记录。
这篇教程假定你安装了Service Pack 1的Visual Studio 2008或者Visual Web Developer 2008。你需要Service Pack 1来使用实体框架。你可以从下面的地址安装 Visual Studio 2008 Service Pack 1或者带有Service Pack 1的 Visual Web Developer:
ASP.NET MVC和微软实体框架没有什么实质的联系。已有几个可以供ASP.NET MVC框架使用的实体框架。例如:你可以使用诸如Microsoft LINQ to SQL、NHibernate又或者SubSonic的其它O/RM工具创建你的MVC模型类。
创建Moive例子数据库
Moive数据库应用使用一个名叫Movies包含了下列栏目的数据库表:
栏目名称 |
数据类型 |
允许空? |
主键? |
Id | int | False | 真 |
Title | nvarchar(100) | False | 假 |
Director | nvarchar(100) | False | 假 |
你可以通过下列的步骤添加这个表到ASP.NET MVC项目:
- 在解决方案浏览器窗口里右键单击App_Data文件夹,并选择菜单项"添加"、"新项"
- 从添加新项的对话框中,选择SQL Server数据库,以MoviesDB.mdf命名该数据库,然后点击确定按钮
- 双击MoviesDB.mdf文件来打开服务器浏览器/数据库浏览器窗口。
- 展开MoivesDB.mdf数据库连接,右键单击表文件夹,并选择菜单项添加新表。
- 在表设计器里,添加Id、Title和Director列。
- 点击保存按钮(有个磁盘图标的那个按钮),以Movies为名字保存新表。
在你创建Movies数据库表后,你应该添加一些简单的数据到表里。右键单击Movies表并选择菜单项显示表数据。你可以在出现的表里输入假的电影数据
创建ADO.NET实体数据模型
要使用微软实体框架,你需要创建一个实体数据模型。可以利用Visual Studio实体数据模型向导来从一个数据库自动地生成实体数据模型。
照着下面的步骤:
- 在解决方案浏览器窗口里右键单击Models文件夹,然后选择菜单项添加,新项。
- 在添加新项对话框里,选择数据类(如图1)。
- 选择ADO.NET实体数据模型模板,给这个实体数据模型命名为MoviesDBModel.edmx,然后点击添加按钮。点击添加按钮的时候会载入数据模型向导。
- 在选择模型内容步骤,选择从数据库生成选项,然后点击下一步按钮(如图2)。
- 在选择数据连接步骤,选择MoviesDB.mdf数据库连接,输入实体连接设置名字为MoviesDBEntities,然后点击下一步按钮(如图3)。
- In the Choose Your Database Objects step, select the Movie database table and click the Finish button (see 图 4).
- 在选择数据对象中,选择Movie数据库表然后点击完成按钮(如图4)。
在完成这写步骤后,ADO.NET 实体数据模型设计器(实体设计器)打开了。
图 1 --创建一个新的实体数据模型
图 2 -- 选择数据内容步骤
图 3 --选择数据连接
图 4 - 选择数据库对象
修改ADO.NET实体数据模型
在创建完实体数据模型后,你可以修改通过实体设计器来修改模型(如图5)。你可以在任何时候通过双击在解决方案浏览器里的Models 文件夹的MoviesDBModel.edmx来打开实体设计器。
图 5 -- ADO.NET 实体数据模型设计器
例如,你可以使用实体设计器来改变实体模型数据向导生成的类的名字。向导创建了一个新的名叫Movies的数据接入类。换句话说,向导给了这个类和数据库表一样的名字。由于我们将会使用这个类来表示一个特定的电影实例,我们应该将Movies重命名为Movie。
如果你想重命名一个实体类,你可以双击实体设计器里的类的名字然后输入一个新的名字(如图6).或者,你可以在实体设计器里选择一个实体后在属性窗口里修改这个实体的名字。
图 6 -- 改变一个实体的名字
记住在修改后保存你的实体数据模型。在这个场景后面,实体设计器生成了一系列的C#类。你可以在解决方案浏览器窗口里通过打开MoviesDBModel.Designer.cs文件看到这些类。
别修改Designer.cs里面的代码,因为你的改动着下一次使用实体设计器的时候会被覆写。如果你想要扩展在Designer.cs里面的实体类的功能,你可以中单独的文件里创建部分类(partial classes)。
通过微软实体框架选择数据库数据
让我们从一个显示电影记录的列表的页面开始我们的Movie数据应用程序开发吧。清单1的Home控制器暴露了一个名叫的Index()的行为。Index()行为通过微软实体框架返回了所有Movie数据库表的记录。
清单 1 - Controllers\HomeController.cs
using System.Web.Mvc;
using MovieEntityApp.Models;
namespace MovieEntityApp.Controllers
{
[HandleError]
public class HomeController : Controller
{
MoviesDBEntities _db;
public HomeController()
{
_db = new MoviesDBEntities();
}
public ActionResult Index()
{
ViewData.Model = _db.MovieSet.ToList();
return View();
}
}
}
注意清单1的控制器包含了一个构造函数。该构造函数初始化了一个名叫_db类-级的字段。_db字段表示的是微软实体框架生成的数据库实体。_db字段是MoviesDBEntities类的一个实例。它由实体设计器生成。
为了使用Home控制器的MoviesDBEntites类,你必须引入MovieEntityApp.Models命名空间(MVC项目名.Models)。
_db字段中Index()行为里被使用来检索数据库表的记录。_db.MovieSet语句代表了数据库表里的所有记录。ToList()方法被用以转换电影的记录集到一个泛型的Movie对象集合 (List<Movie>)。
通过LINQ to Entites的帮助,电影记录被检索了。清单1的Index()行为使用了LINQ方法语法来检索数据库记录的集合。如果你喜欢,你可以使用LINQ查询语法来代替。下面的两句话完成了相同的工作:
ViewData.Model = _db.MovieSet.ToList();
ViewData.Model = (from m in _db.MovieSet select m).ToList();
使用whichever LINQ语法--函数语法或查询语法,你觉得最直观的那种。这两种方法没有什么性能上的差别--不同的只是风格。
清单2的视图被用来显示电影记录。
清单 2 - Views\Home\Index.aspx
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Index</title>
</head>
<body>
<div>
<% foreach (var m in ViewData.Model)
{ %>
Title: <%= m.Title %>
<br />
Director: <%= m.Director %>
<br />
<%= Html.ActionLink("Edit", "Edit", new { id = m.Id })%>
<%= Html.ActionLink("Delete", "Delete", new { id = m.Id })%>
<hr />
<% } %>
<%= Html.ActionLink("Add Movie", "Add") %>
</div>
</body>
</html>
清单2的视图包含了一个 foreach 循环。它枚举了每一个电影记录并显示了每一个电影记录的Title和Director属性。注意到编辑和删除连接是显示在每一个记录的附近的。此外,一个添加连接在视图的底部出现(如图7)。
图 7 - The Index view
Index视图是一个类型化的视图。Index视图包含了一个<%@Page%>指令。该指令的Inherts属性投射了Model属性到一个强类型的泛型Movies对象集合(List<Movie>)。
通过微软实体框架插入数据库记录
你可以使用微软实体框架来让插入新记录到一个数据库表变得很容易。清单3包含来你可以用以插入新记录到Movie数据库表的两个新加入到Home控制器的行为。
清单 3 - Controllers\HomeController.cs (Add methods)
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Add(FormCollection form)
{
var movieToAdd = new Movie();
// Deserialize (Include white list!)
TryUpdateModel(movieToAdd, new string[] { "Title", "Director" }, form.ToValueProvider());
// Validate
if (String.IsNullOrEmpty(movieToAdd.Title))
ModelState.AddModelError("Title", "Title is required!");
if (String.IsNullOrEmpty(movieToAdd.Director))
ModelState.AddModelError("Director", "Director is required!");
// If valid, save movie to database
if (ModelState.IsValid)
{
_db.AddToMovieSet(movieToAdd);
_db.SaveChanges();
return RedirectToAction("Index");
}
// Otherwise, reshow form
return View(movieToAdd);
}
第一个Add()行为简单地返回了一个视图。该视图包含了一个添加一个新电影数据库记录的表单(如图8).在提交该表单后,第二个Add()行为被调用了。
注意第二个Add()行为是被AccetpVerbs标签装饰的。这个行为当且仅当HTTP POST操作被执行的时候可以被调用。换句话说,这个行为只能在提交一个HTML表单的时候被调用。
第二个Add()行为中ASP.NET MVC的TryUpdateModel()帮助下创建了一个Movie类的新实例。TryUpdateModel()从传递到Add()函数的FormCollection里去的字段的值然后将这些HTML表单的值和Movie类的属性一一对上。
在使用微软实体框架的时候,在使用TryUpdateModel或UpdateModel方法来更新实体类的属性的值的时候你必须提供一个属性的"白名单"
接下来,Add()行为执行了一些简单的验证。行为验证了标题和导演的值都有值。如果有验证错误,则一个验证错误消息会被添加到ModelState。
如果没有验证错误,那么一个新的电影记录就会通过微软实体框架的帮助添加到数据库表。新的数据库记录会由下面的两行代码添加到数据库里:
_db.AddToMovieSet(movieToAdd);
_db.SaveChanges();
第一行的代码添加新的Movie实体到微软实体框架追踪的电影集合。第二行的代码保存了Movies里面所有的改动到底层的数据库。
图 8 -- 添加视图
使用微软实体框架更新数据库记录
你可以沿着几乎和我们刚插入新记录到数据那样完全一样的方法去使用微软实体框架来编辑一个数据库记录。清单4包含了两个名叫Edit()的控制器行为。第一个Edit()行为返回了一个HTML表单来编辑一个电影记录。第二个Edit()行为则尝试更新数据库。
清单 4 - Controllers\HomeController.cs (Edit methods)
{
// Get movie to update
var movieToUpdate = _db.MovieSet.First(m => m.Id == id);
ViewData.Model = movieToUpdate;
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection form)
{
// Get movie to update
var id = Int32.Parse(form["id"]);
var movieToUpdate = _db.MovieSet.First(m => m.Id == id);
// Deserialize (Include white list!)
TryUpdateModel(movieToUpdate, new string[] { "Title", "Director" }, form.ToValueProvider());
// Validate
if (String.IsNullOrEmpty(movieToUpdate.Title))
ModelState.AddModelError("Title", "Title is required!");
if (String.IsNullOrEmpty(movieToUpdate.Director))
ModelState.AddModelError("Director", "Director is required!");
// If valid, save movie to database
if (ModelState.IsValid)
{
_db.SaveChanges();
return RedirectToAction("Index");
}
// Otherwise, reshow form
return View(movieToUpdate);
}
第二个Edit()行为通过从数据库检索Id符合正在编辑的记录的Id的电影记录开始。下面的LINQ to Entities语句获得了符合特定Id值的数据库记录:
var movieToUpdate = _db.MovieSet.First(m => m.Id == id);
接下来,TryUpdateModel()方法被用来分配HTML表单里的值到电影实体的属性。注意到一个白名单被提供用以指定准确的要更新的属性。
接下来,一些简单的严重被执行用以验证电影的标题和导演属性都有值。如果有一个属性没有值,那么一个严重错误消息就会被添加到ModelState并且ModelState.IsValid返回一个false值。
最后,如果没有验证错误,那么底层的数据表会在调用SaveChanges()方法的时候更新所有变动。
在编辑数据库记录的时候,你需要传递记正在被编辑的记录的Id值到执行数据更新的控制器行为。否则,控制器行为无法知道要更新底层数据库的哪条记录。清单5的编辑视图,包含了一个表示正在被编辑的数据库记录的Id值的隐藏域。
清单 5 - Views\Home\Edit.aspx
<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<MovieEntityApp.Models.Movie>" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Edit</title>
<style type="text/css">
.input-validation-error
{
background-color:Yellow;
}
</style>
</head>
<body>
<div>
<h1>Edit Movie</h1>
<form method="post" action="/Home/Edit">
<!-- Include Hidden Id -->
<%= Html.Hidden("id") %>
Title:
<br />
<%= Html.TextBox("title") %>
<br /><br />
Director:
<br />
<%= Html.TextBox("director") %>
<br /><br />
<input type="submit" value="Edit Movie" />
</form>
</div>
</body>
</html>
使用微软实体框架来删除数据库记录
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Edit</title>
<style type="text/css">
.input-validation-error
{
background-color:Yellow;
}
</style>
</head>
<body>
<div>
<h1>Edit Movie</h1>
<form method="post" action="/Home/Edit">
<!-- Include Hidden Id -->
<%= Html.Hidden("id") %>
Title:
<br />
<%= Html.TextBox("title") %>
<br /><br />
Director:
<br />
<%= Html.TextBox("director") %>
<br /><br />
<input type="submit" value="Edit Movie" />
</form>
</div>
</body>
</html>
在这篇教程里我们需要对付的最后的数据库操作是删除数据库记录。你可以使用清单6的控制器行为来删除一个特定的数据库记录。
清单 6 -- \Controllers\HomeController.cs (Delete action)
{
// Get movie to delete
var movieToDelete = _db.MovieSet.First(m => m.Id == id);
// Delete
_db.DeleteObject(movieToDelete);
_db.SaveChanges();
// Show Index view
return RedirectToAction("Index");
}
Delete()行为首先从Movie实体检索符合传入的Id的实体。接下来,该电影通过调用DeleteObject()方法和SaveChanges()方法被从数据库中删除。最后,用户被重定向到Index视图。
总结
这篇教程的目的是演示如何通过利用ASP.NET MVC和微软实体框架的优势来创建数据驱动的Web应用。你可以学习到如何开发一个允许你选择、插入、更新和删除数据库记录的应用。
首先,我们讨论了如何用Visual Studio的实体数据模型向导来生成一个实体数据模型。接下来,你学习了如何使用LINQ to Entities来从一个数据库表检索一个数据库记录的集合。最后,我们使用了微软实体框架来插入、更新和删除数据库记录。