Asp.Net MVC 模型(使用Entity Framework创建模型类) - Part.1

这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework来创建数据访问类。这篇教程假设你事先对Microsoft Entity Framework没有任何的了解。读完本篇教程,你将会理解如何使用Entity Framework来选择、插入、更新和删除数据库记录。

Microsoft Entity Framework是一个对象关系映射(O/RM)工具,它能你让自动从数据库生成数据访问层。Entity Framework能够使你免于手工编写冗长的数据访问类。

为了演示如何在ASP.NET MVC中使用Microsoft Entity Framework,我们将会创建一个简单的范例应用程序。我们将会创建一个Movie数据库应用程序,它允许你显示和编辑Movie数据库记录。

这篇教程假设你拥有Visual Studio 2008或者Visual Web Developer 2008及Service Pack1。为了使用Entity Framework,你需要使用Service Pack1。你可以从下面的地址下载Visual Studio 2008 Service Pack1或者含有Service Pack1的Visual Web Developer:

http://www.asp.net/downloads/

NOTE:在ASP.NET MVC和Microsoft Entity Framework之间没有什么必然的联系。在ASP.NET MVC中除了使用Entity Framework,你还有几个可选的方式。举个例子,你可以使用其他的O/RM工具,例如Microsoft LINQ to SQL,NHibernate或者SubSonic来创建你的MVC模型类。

1.创建Movie范例数据库

Movie数据库应用程序使用叫做Movies的数据库表格,它包含下面的列:

列名 数据类型 允许Null 是否主键
Id int False True
Title nvarchar(100) False False
Director nvarchar(100) False False

你可以使用下面步骤将这个表添加到ASP.NET MVC项目中:

  1. 右键点击“解决方案浏览器”中的App_Data文件夹,并且选择菜单项“添加”,“新建项”。
  2. 在“添加新项(Add New Item)”对话框,选择“SQL Server Database”,将数据库命名为MoviesDB.mdf,并且点击“添加(Add)”按钮。
  3. 双击MoviesDB.mdf文件,打开“服务器资源管理器/数据库资源管理器”窗口。
  4. 展开MoviesDB.mdf数据库连接,右键点击“表”文件夹,并且选择菜单选项“添加新表(Add New Table)”。
  5. 在表设计器中,添加Id、Title和Director列。
  6. 点击“保存(Save)”按钮(它有一个软盘的图标)将新表保存为Movies。

在你创建好Movies数据库表之后,你应该为表中添加一些范例数据。右键点击Movies表,并且选择菜单项“显示表数据(Show Table Data)”。你可以向显示的网格中输入一些虚构的电影数据。

2.创建ADO.NET实体数据模型

为了使用Entity Framework,你需要创建一个实体数据模型(Entity Data Model)。你可以利用Visual Studio实体数据模型向导(Entity Data Model Wizard)来自动从数据库中生成一个实体数据模型。

按照下面的步骤:

  1. 右键点击“解决方案浏览器”窗口中的Models文件夹,并且选择菜单项,“添加(Add)”,“新建项(New Item)”。
  2. 在“添加新项(Add New Item)”对话框中,选择Data分类(如图1)。
  3. 选择ADO.NET Entity Data Model模板,将实体数据模型命名为MoviesDBModel.edmx,并且点击“添加”按钮。点击“添加”按钮将会运行数据模型向导。
  4. 在“选择模型内容(Choose Model Contents)”步骤,选择“从数据库生成(Generate from a database)”选项,并且点击“下一步(Next)”按钮(如图2)。
  5. 在“选择数据连接(Choose Your Data Conncetion)”步骤,选择MoviesDB.mdf数据库连接,输入实体的连接设置名MoviesDBEntities,并且点击“下一步”按钮(如图3)。
  6. 在“选择你的数据库对象(Choose Your Database Object)”步骤,选择Movie数据库表,并且点击“完成”按钮(如图4)。

在你完成了这些步骤以后,将会打开“ADO.NET实体数据模型设计器(实体设计器)”。

图1 - 创建一个新的实体数据模型

图2 - 选择模型内容步骤

图3 - 选择数据连接

图4 - 选择你的数据库对象

3.修改ADO.NET实体数据模型

在创建好实体数据模型以后,你可以利用实体设计器(如图5)来修改模型。通过双击“解决方案浏览器”中Models文件夹中的MoviesDBModel.edmx文件,你可以在任何时候打开实体设计器。

图5 - ADO.NET 实体数据模型设计器

举个例子,你可以使用“实体设计器”来更改“实体模型数据向导”生成的类名。向导创建了一个叫做Movies的新的数据访问类。换言之,Wizard对这个类的命名与数据库中的表名完全一致。因为我们将会使用这个类来代表特定的Movie实例,我们应该将这个类由Movies重命名为Movie。

如果你想要重命名实体类,你可以在实体设计器的类名上双击,并且输入一个新的名字(如图6)。或者,你可以在实体设计器中选择一个实体,然后在属性窗口中修改实体类的名字。

图6 - 更改一个实体名

记得在做出修改后点击“保存”按钮来保存你的“实体数据模型”。在幕后,实体设计器会生成一系列的C#类。通过从“解决方案资源管理器”窗口打开MoviesDBModel.Designer.cs文件,你可以查看这些类。

NOTE:不要在Designer.cs文件中修改代码,因为在下次使用实体设计器时,你的变更将会被覆盖。如果你想要扩展定义在Designer.cs中的实体类的功能,那么你可以在单独的文件中创建部分类。

4.使用Entity Framework选择数据库记录

让我们通过创建一个显示movie记录列表的页面来开始构建我们的Movie数据库应用程序。代码1中的Home控制器发布了一个名为Index()的动作。通过使用Entity Framework,Index()动作返回了来自Movie数据库表的所有movie记录。

代码清单1 - Controllers\HomeController.cs

using System.Linq;
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字段代表着由Microsoft Entity Framework生成的数据库实体。_db字段是一个MoviesDBEntities类的实例,该实例由“实体设计器”生成。

NOTE:为了在Home控制器中使用MoviesDBEntites类,你必须引入MovieEntityApp.Models命名空间。

在Index()动作中使用了_db字段,用于获取Movies数据库表中的记录。表达式_db.MovieSet代表了所有来自Movies数据库表的记录。ToList()方法用于将movies记录集转换为一个Movie对象的泛型集合(List<Movie>)。

movie记录在LINQ to Entities的帮助下获得。代码1中的Index()动作使用了LINQ方法语法(method syntax)来获取数据库的记录集。如果你愿意,你也可以使用LINQ查询语法(query syntax)。下面两行语句完成了同样的事情:

ViewData.Model = _db.MovieSet.ToList();
ViewData.Model = (from m in _db.MovieSet select m).ToList();

使用你最有感觉的任意一种LINQ语法――方法语法或者查询语法。这两种方法在性能上没有任何区别――唯一的区别是风格。

代码清单2中的视图用于显示movie记录。

代码清单2 - Views\Home\Index.aspx

<%@ Page Language="C#" 
  Inherits="System.Web.Mvc.ViewPage<List<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>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循环,它遍历了每一个movie记录,并且显示了movie记录的Title和Director属性的值。注意到在每个记录旁边显示了Edit和Delete链接。除此以外,Add Movie链接显示在视图的底部(如图7)。

图7 - Index 视图

Index视图是一个类型化视图(typed view)。Index视图包含了一个<%@ Page %>指示符,其中含有一个Inherits属性,它将Model属性强制转换为了一个Movie对象的强类型泛型列表集合(List<Movie>)。

5.使用Entity Framework插入数据库记录

你可以使用Entity Framework轻松地将新纪录插入到数据库表中。代码清单3包含了两个添加到Home控制器类中的新动作,你可以使用它们来将新纪录插入到Movie数据库表中。

代码清单3 - Controllers\HomeController.cs(Add方法)

public ActionResult Add() {
    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()动作简单地返回了一个视图。这个视图包含了一个用于添加新movie数据库记录的表单(如图8)。当你提交表单时,将会调用第二个Add()动作。

注意到第二个Add()动作使用AcceptVerbs特性进行了修饰。这个动作只有在执行HTTP POST操作的时候才会被调用。换言之,这个动作只有在提交一个HTML表单的时候会被调用。

第二个Add()动作在ASP.NET MVC TryUpdateModel()方法的帮助下创建了一个Entity Framework Movie类的新实例。TryUpdateModel()方法接受传递给Add()方法的FormCollection的字段,并且将这些HTML表单字段的值赋值给Movie类。

NOTE:使用Entity Frmaework时,当用TryUpdateModel或者UpdateModel方法更新一个实体类的属性时,你必须提供一个属性的“白名单”。

接下来,Add()动作执行了一些简单的表单验证。该动作验证了Title和Director属性都有值。如果出现一个验证错误,那么一个验证错误消息将会添加到ModelState。

如果没有验证错误,那么在Entity Framework的帮助下,一个新的movie记录将会添加到Movies数据库表。新的记录使用下面两行代码添加到数据库中:

_db.AddToMovieSet(movieToAdd);
_db.SaveChanges();

代码的第一行将新的Movie实体添加到由Entity Framework所追踪的movies集中。代码的第二行将所有对Movies的更改保存到底层的数据库中。

图8 - Add 视图

6.使用Entity Framework更新数据库记录

你几乎可以按照与我们刚才插入一个新数据库记录相同的办法来使用Entity Framework编辑一条数据库记录。代码清单4包含了两个新的控制器动作,叫做Edit()。第一个Edit()动作返回了一个HTML表单,用于编辑一条movie记录。第二个Edit()动作试图更新数据库。

代码清单4 - Controllers\HomeController.cs(Edit方法)

public ActionResult Edit(int id)
{
    // 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()动作开始时从数据库中获取一条与要编辑的movie的Id相匹配的Movie记录。下面的LINQ to Entities语句获取了匹配这一特定Id的第一条数据库记录。

var movieToUpdate = _db.MovieSet.First(m => m.Id == id);

接下来,TryUpdateModel()方法用于将HTML表单域的值赋给movie实体的属性。注意到提供了一个白名单,用于指定更新哪些字段。

下一步,执行了一些简单的校验来验证Movie的Title和Director属性都有值。如果任何一个属性缺少值,那么一个校验错误消息将会添加到ModelState,并且ModelState.IsValid将会返回false值。

最后,如果没有校验错误,那么通过调用SaveChanges()方法,底层的Movies数据库表将会更新。

当编辑数据库记录时,你需要向执行数据库更新的控制器动作传递要进行更新的记录的Id。否则,控制器动作将无法知道更新底层数据库的哪一个记录。代码清单5中的Edit视图,含有一个隐藏的表单域,它代表了将要编辑的数据库记录的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>

7.使用Entity Framework删除数据库记录

在这篇教程中我们所需解决的最后一个数据库操作,就是删除数据库记录了。你可以使用代码清单6中的控制器动作来删除一个特定的数据库记录。

代码清单6 - \Controllers\HomeController.cs(删除操作)

public ActionResult Delete(int id) {
    // 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()方法,Movie从数据库中删除掉了。最终,用户被重定向到了Index视图。

8.总结

本篇教程的目的是演示如何利用ASP.NET MVC和Microsoft Entity Framework来创建数据库驱动的Web应用程序。你学习到了如何创建应用程序,允许你进行选择、插入、更新和删除数据库记录。

首先,我们讨论了如何使用实体数据模型向导(Entity Data Model Wizard)从Visual Studio中生成一个实体数据模型。接下来,你学习了如何使用LINQ to Entities来从一个数据库表中获取一系列数据库记录。最后,我们使用了Entity Framework来插入、更新和删除数据库记录。