您的位置:首页 > 编程语言 > ASP

为ASP.NET MVC应用创建Entity Framework数据模型(1/10)

2012-04-26 14:33 876 查看
原址:http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application

这个简单的Contoso大学Web应用演示了怎么使用Entity Framework去创建一个ASP.NET MVC应用。这是为虚构的Contoso大学构建的一个简单网站应用。它包括了例如学生入档,课程创建,教师分配等功能。
这个系列指导说明了开始构建一个Contoso大学简单应用的相关步骤。您能下载完成后的应用或者跟着教程的步骤创建它。这个教程使用C#来展现例子,下载的示例中包含c#和Visual Basic。如果您有与这个教程没有直接关联的问题,可以把它们发送到ASP.NET
Entity Framework forum或Entity Framework and LINQ to Entities forum
这个系列教程假设您知道怎么在Visual Studio中使用ASP.NET MVC,如果您不会使用,basic ASP.NET MVC Tutorial是一个很好的学习地方。如果您更喜欢使用ASP.NET
Web Forms模式工作,可以看看Getting Started with the Entity Framework和Continuing
with the Entity Framework教程。
在开始之前,请确保在您的计算机中有安装下列必备软件:

Visual Studio 2010 SP1 或Visual
Web Developer Express 2010 SP1 (如果您使用二者之一,下面的工具会自动安装。)
ASP.NET MVC 3 Tools Update
Microsoft SQL Server Compact 4.0
Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0

Contoso大学Web应用

您将会在这次教程中创建一个简单的大学网站应用。



使用者能够查看和更新学生,课程以及教师信息。下面是一些创建后的截图。







站点的UI风格会跟自动生成的内置模板保持一致,以便这个教程能将重点聚焦到怎么去使用Entity Framework上。

Entity Framework开发方法

正如下面图表展示的,在Entity Framework中您有三种方式来开始工作:数据库优先(Database First),模型优先(Model First)和代码优先(Code First)。



数据库优先(Database First)

如果您已经有一个数据库,Entity Framework能自动的生成一个充斥着跟已存在数据库中对象(如表和列)相一致的类和属性的数据模型。数据库的结构信息,您的数据模型和他们之间的映射都会存储在以.edmx为后缀的XML文件中。Visual Studio提供了Entity Framework图形设计器,您能够使用它显示和编辑.edmx文件。在Web Forms系列教程中会用数据库优先(Database First)的章节有Getting
Started With the Entity Framework和Continuing With the Entity Framework

模型优先(Model First)

如果您至今没有一个数据库,您能在Visual Studio中使用Entity Framework设计器创建一个模型,当模型完成了,设计器能生成DDL(data definition language)语句去创建一个数据库。这个方法也使用了一个.edmx文件来存储模型和映射信息。What's
New in the Entity Framework 4教程包含了一个简短的模型优先(Model First)的例子。

代码优先(Code First)

不论您是否拥有一个已经存在的数据库,您能使用代码书写自己的类和属性跟数据库中的表,列保持一致,并且在Entity Framework中使用这种方式不会生成一个.edmx文件。这就是为什么有时您会看到这种方法也被称为"code only",虽然官方名称是代码优先(Code First)。存储结构和概念模型的映射代表了您的代码根据一些约束和特殊的映射API来处理。如果您至今没有一个数据库,Entity Framework能自动为您创建数据库或当模型发生改变后,删除并重新创建它。这个系列教程都会只用代码优先(Code
First)方法来开发。

这个数据操作API是为基于DbContext类的代码优先(Code First)而开发。这个API也能被数据库优先(Database First)和模型优先(Model First)所使用。更多的信息,请参阅Entity Framework小组的博客When
is Code First not code first?。

POCO (Plain Old CLR Objects)

默认的,当您使用数据库优先(Database First)或模型优先(Model First)开发方法,数据模型中的实体类继承自EntityObject类,这个基类提供了Entity Framework的功能。这意味着这些类不能随意的使用,也就不能很舒服的符合领域驱动设计(domain-driven
design)的要求。Entity Framework的所有开发方法都能和POCO (plain old CLR objects)类一起工作,实质上意味着它们可以随意使用,因为它们不继承自
EntityObject
类。在教程中,您会使用POCO类。

在开始之前,请确保在您的计算机中有安装下列必备软件:

Visual Studio 2010 SP1 或Visual
Web Developer Express 2010 SP1 (如果您使用二者之一,下面的工具会自动安装。)
ASP.NET MVC 3 Tools Update
Microsoft SQL Server Compact 4.0
Microsoft Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0

打开Visual Studio并创建一个新的ASP.NET MVC 3 Web Application模板项目,命名为"ContosoUniversity":



在这个New ASP.NET MVC 3 Project对话框中选择Internet Application模板和Razor视图引擎,清空Create a unit test project选择框,然后单击OK。



设置站点风格

一些简单的改变用与设置站点菜单,布局和home页面。

为了设置Contoso大学菜单,在Views\Shared\_Layout.cshtml文件中,替换已经存在的h1头文本和菜单链接,如下所示:

<!DOCTYPE html>
<html>
<head>
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
</head>
<body>
<div class="page">
<div id="header">
<div id="title">
<h1>Contoso University</h1>
</div>

<div id="logindisplay">
@Html.Partial("_LogOnPartial")
</div>
<div id="menucontainer">
<ul id="menu">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Students", "Index", "Student")</li>
<li>@Html.ActionLink("Courses", "Index", "Course")</li>
<li>@Html.ActionLink("Instructors", "Index", "Instructor")</li>
<li>@Html.ActionLink("Departments", "Index", "Department")</li>
</ul>
</div>
</div>
<div id="main">
@RenderBody()
</div>
<div id="footer">
</div>
</div>
</body>
</html>

在Views\Home\Index.cshtml文件中,删除h2标签下的所有内容。

在Controllers\HomeController.cs文件中,修改成"Welcome to Contoso University!"来替换原有的 "Welcome to ASP.NET MVC!" 。

在Content\Site.css文件中,为了使菜单标签页靠左,像下面一下修改样式文件:

在定义的#main中,增加clear: both;,如下所示:
#main
{
clear: both;
padding: 30px 30px 15px 30px;
background-color: #fff;
border-radius: 4px 0 0 0;
-webkit-border-radius: 4px 0 0 0;
-moz-border-radius: 4px 0 0 0;
}


在定义的nav 和
#menucontainer
中,增加clear: both; float: left;,如下所示:
nav,
#menucontainer {
margin-top: 40px;
clear: both;
float: left;
}


启动网站。您看到home页面中的主菜单如下所示。



创建数据模型

接下来您将为Contoso大学应用创建实体类,先从下面三个实体开始:



Student和
Enrollment
实体是一对多的关系,并且,Course和Enrollment实体也是一对多的关系,换句话说,一个学生能注册多门课程,并且一门课程能被多名学生注册。

在下面的章节中,您将会为每个实体创建相应的类。

:如果您还没有创建所有实体类,就尝试编译项目,这会导致编译错误。

Student实体



在Models文件夹中,创建Student.cs文件,用下面的代码来替换原有代码:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
public class Student
{
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}

StudentID属性将变成和类相一致的数据库表中的主键列。默认,Entity Framework会把属性名是ID或classname +ID的属性作为主键。

Enrollments是一个导航属性,导航属性把其他实体关联到这个实体,一个Student实体的Enrollments属性用来保存所有跟这个Student实体相关的Enrollment实体,换句话说,如果数据库中有一个给定的Student行被关联了两个Enrollment行(Student主键值对应这个Enrollment表中StudentID外键列),那么这个Student实体的Enrollments导航属性将包含那两个Enrollment实体。

导航属性作为
virtual
被定义,以便让Entity Framework给它们提供一种被称作延迟加载的功能特性。(延迟加载会在之后说明,在这个系列教程的Reading
Related Data中)。如果一个导航属性需要保存多个实体,那么它的类型必须是实现了ICollection接口。

Enrollment实体



在Models文件夹中,创建Enrollment.cs文件,用下面的代码来替换原有代码:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public decimal? Grade { get; set; }
public virtual Course Course { get; set; }
public virtual Student Student { get; set; }
}
}

在decimal类型的后面标注了一个?,表明Grade属性是可空的。一个可空的Grade和一个为0的Grade是不同的—空意味着一个Grade没有被附过值,而0则意味着这个Grede被附加了0这个值。

这个StudentID是一个外键,并且与Student中的导航属性保持一致,一个Enrollment实体和一个Student实体有所关联,因此这个属性仅能保存单个Student实体。(不像您之前看到的Student.Enrollments导航属性,能保存多个Enrollment实体。)

这个CourseID属性是一个外键,并且与Course中的导航属性保持一致,一个Enrollment实体和一个Course实体有所关联。

Course实体



在Models文件夹中,创建Course.cs文件,用下面的代码来替换原有代码:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
public class Course
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}

这个Enrollments是一个导航属性,一个Course实体可以关联到多个Enrollment实体。

创建数据库上下文(Context)

这个用于为一个给定的数据模型协调Entity Framework功能的总类是数据库上下文(context)类。您创建的这个类派生自System.Data.Entity.DbContext类,在您的代码中,您指定了把实体包含到数据模型中,您也能自定义Entity Framework的行为。在这个项目的代码中,这个类被命名为SchoolContext。

创建一个DAL文件夹。在文件夹中创建一个新类,命名为SchoolContext.cs,之后用下面的代码来替换原有代码:

using System;
using System.Collections.Generic;
using System.Data.Entity;
using ContosoUniversity.Models;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.Models
{
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Course> Courses { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}

代码中为每个实体集创建了一个DbSet属性。在Entity Framework技术中,一个实体集与数据库中的表保持一致并且一个实体与表中的行保持一致。

OnModelCreating方法中的语句阻止了表名被限制成复数。如果您不这么做,生成的表名将会被命名为Students,
Courses
, 和
Enrollments
来取代想要生成的表名Student,
Course
,和
Enrollment,
之所以这样,是因为开发者就表名是否使用复数没有达成一致。这个教程使用了单数形式,但重点是您可以自己选择使用哪种形式来命名。

(这个类在Models命名空间中,因为在一些情况,代码优先(Code First)假设实体类和上下文(context)类是在同一命名空间中。)

设置连接字符串

您不必创建一个连接字符串,如果没有创建,Entity Framework将会为您自动的创建一个SQL Server Express数据库。然而,在这次教程中,您将会用到SQL Server Compact,因此您需要指定一个连接字符串。

打开project Web.config文件并且增加一个新的连接字符串到connectionStrings集合中,如下所示。(确保您更新的是项目根目录下的Web.config文件,在Views子文件夹中也存在Web.config文件,这个文件您不需要更新。)

<add name="SchoolContext" connectionString="Data Source=|DataDirectory|School.sdf" providerName="System.Data.SqlServerCe.4.0"/>

默认,Entity Framework会寻找和您的上下文(context)类名称相同的连接字符串。这个连接字符中,您指定了SQL Server Compact数据库的名称为School.sdf,存放在App_Data文件夹下。

给数据库初始化测试数据

当应用启动时,Entity Framework能为您自动的创建(删除并重新创建)一个数据库。您能在应用中指定每次都重新创建或当模型与已存在数据库不同步的时候在重新创建。为了测试数据,您也能写一个类包含一个方法,当每次重新创建完数据库以后,Entity Framework去自动的调用它。在这章节,您将会指定无论模型是否发生改变,数据库都会被删并重新创建。

在DAL文件夹中,创建一个新类命名为SchoolInitializer.cs,用下面的代码来替换原有代码,当需要和加载测试数据到一个新的数据库时引发数据库的重新创建。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using ContosoUniversity.Models;

namespace ContosoUniversity.DAL
{
public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext>
{
protected override void Seed(SchoolContext context)
{
var students = new List<Student>
{
new Student { FirstMidName = "Carson",   LastName = "Alexander", EnrollmentDate = DateTime.Parse("2005-09-01") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",    EnrollmentDate = DateTime.Parse("2002-09-01") },
new Student { FirstMidName = "Arturo",   LastName = "Anand",     EnrollmentDate = DateTime.Parse("2003-09-01") },
new Student { FirstMidName = "Gytis",    LastName = "Barzdukas", EnrollmentDate = DateTime.Parse("2002-09-01") },
new Student { FirstMidName = "Yan",      LastName = "Li",        EnrollmentDate = DateTime.Parse("2002-09-01") },
new Student { FirstMidName = "Peggy",    LastName = "Justice",   EnrollmentDate = DateTime.Parse("2001-09-01") },
new Student { FirstMidName = "Laura",    LastName = "Norman",    EnrollmentDate = DateTime.Parse("2003-09-01") },
new Student { FirstMidName = "Nino",     LastName = "Olivetto",  EnrollmentDate = DateTime.Parse("2005-09-01") }
};
students.ForEach(s => context.Students.Add(s));
context.SaveChanges();

var courses = new List<Course>
{
new Course { Title = "Chemistry",      Credits = 3, },
new Course { Title = "Microeconomics", Credits = 3, },
new Course { Title = "Macroeconomics", Credits = 3, },
new Course { Title = "Calculus",       Credits = 4, },
new Course { Title = "Trigonometry",   Credits = 4, },
new Course { Title = "Composition",    Credits = 3, },
new Course { Title = "Literature",     Credits = 4, }
};
courses.ForEach(s => context.Courses.Add(s));
context.SaveChanges();

var enrollments = new List<Enrollment>
{
new Enrollment { StudentID = 1, CourseID = 1, Grade = 1 },
new Enrollment { StudentID = 1, CourseID = 2, Grade = 3 },
new Enrollment { StudentID = 1, CourseID = 3, Grade = 1 },
new Enrollment { StudentID = 2, CourseID = 4, Grade = 2 },
new Enrollment { StudentID = 2, CourseID = 5, Grade = 4 },
new Enrollment { StudentID = 2, CourseID = 6, Grade = 4 },
new Enrollment { StudentID = 3, CourseID = 1            },
new Enrollment { StudentID = 4, CourseID = 1,           },
new Enrollment { StudentID = 4, CourseID = 2, Grade = 4 },
new Enrollment { StudentID = 5, CourseID = 3, Grade = 3 },
new Enrollment { StudentID = 6, CourseID = 4            },
new Enrollment { StudentID = 7, CourseID = 5, Grade = 2 },
};
enrollments.ForEach(s => context.Enrollments.Add(s));
context.SaveChanges();
}
}
}

这个Seed方法把数据库上下文(context)对象当做输入参数,并且方法中的代码使用这个对象把新的实体添加到数据库中。代码为每个实体类型创建了一个新的实体集合,之后把这个改变保存到数据库中。把每个实体都添加以后,调用SaveChanges方法不是必须的,在这里,我们还是调用了,当正在将数据写入到数据库中,如果引发了一个异常,这么做可以帮助您定位到这个问题源。

在Global.asax.cs文件中做出如下改变,这样当应用启动时,会引发初始化代码:

增加using
声明:
using System.Data.Entity;
using ContosoUniversity.Models;
using ContosoUniversity.DAL;


在Application_Start方法中,调用一个Entity Framework方法来跑起数据库初始化类:
Database.SetInitializer<SchoolContext>(new SchoolInitializer());


当您在第一次启动应用时,Entity Framework将数据库和模型进行对比,如果有所不同,应用将会删除并重新创建数据库。

注:当在生产Web服务器中部署这个应用,您必须移除生成数据库测试数据的代码。

现在,您将创建一个web页面来显示数据,并且请求数据的过程将会自动的触发数据库创建操作。您将创建一个新的控制器controller),但在做这之前,构建有效的模型(model)和上下文(context)类用于MVC自动生成的控制器(controller)。

创建Student控制器(Controller)

要创建Student控制器(controller),在Solution ExplorerControllers文件夹上右击,选择Add,然后点击Controller,在弹出的Add Controller对话框中,按照下面的选项进行选择,最后单击Add

Controller name: StudentController.
Template: Controller with read/write actions and views, using Entity Framework. (默认)
Model class: Student (ContosoUniversity.Models). (如果在下拉框中没有看到这个选项,重新编译项目,然后在尝试一下。)
Data context class: SchoolContext (ContosoUniversity.Models).
Views: Razor (CSHTML). (The default.)



打开Controllers\StudentController.cs文件,您会看到类中已经创建了数据库上下文(context)对象的实例:

private SchoolContext db = new SchoolContext();

这个Index的action方法是从数据库上下文(context)实例的Students属性中,获得学生列表:

public ViewResult Index()
{
return View(db.Students.ToList());
}

自动生成工具也创建了一个Student视图(views),进入到Index视图(view)来自定义默认的标题和列,打开Views\Student\Index.cshtml文件并且用下面的代码来替换原有代码:

@model IEnumerable<ContosoUniversity.Models.Student>

@{
ViewBag.Title = "Students";
}

<h2>Students</h2>

<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
<th>Last Name</th>
<th>First Name</th>
<th>Enrollment Date</th>
</tr>

@foreach (var item in Model) {
<tr>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
@Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
@Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
</td>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
</tr>
}

</table>

启动网站,点击Students标签页,之后您会看到学生列表。



关闭浏览器。在Solution Explorer中,选择ContosoUniversity项目(确保项目而不是解决方案被选中)。单击Show all Files,单击Refresh,之后展开App_Data文件夹,会看到School.sdf文件。



Double-click双击 School.sdf 打开Server Explorer。然后展开Tables文件夹,会看到所有的表已经创建到数据库中。

:当双击School.sdf时,如果发生错误,请确保您有安装Visual Studio 2010 SP1 Tools for SQL Server Compact 4.0。(在顶部的必备软件中,有软件下载链接)。如果您刚刚安装了这个工具,必须要重启Visual Studio才能生效。



每个实体集都会生成一个表,以及外加一个额外的表EdmMetadata,这个表用于检查数据库与模型的同步。

右击其中的一个表,选择Show Table Data,会看到根据SchoolInitializer类初始化的数据已经加载到表中。



当你执行完操作,关闭连接。(如果没有关闭连接,在您下载启动项目时,可能会获得一个错误。)



约定

由于使用了约定和假设,您在Entity Framework中使用极少的井然有序的代码就可以创建一个完备的数据库,下面是一些需要注意的地方:

实体类名称的复数形式会作为表的名称使用。
实体属性名称常常用作列名。
Entity Framework会把属性名是ID或classname +ID的属性作为主键。
Entity Framework会寻找和您上下文(context)类名称相同的连接字符串去连接到数据库 (在这里是
SchoolContext
).

正如您之前看到的,约定是能够被重写的(例如,您指定了表名不必使用复数形式),在之后系列教程的Creating a More Complex Data Model中,您将学会更多的约定和怎么去重写它们。

您现在使用Entity Framework和SQL Server Compact创建了一个简单的应用去存储和显示数据,在接下来的教程中,您将会学习到怎么去执行基于CRUD(create, read, update, delete)的操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: