本系列教程是微软asp.net网站上的教程翻译,原文地址:http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application 。
我们将用EF6和mvc5技术完成一个应用程序Contoso University。这是一个虚拟的项目。里面包含学生管理,成绩管理,课程创建和教师布置作业等功能。本系列教程使用的是code first工作流。mvc中有Code First, Database First, 和Model First三种方式,如何选择适合自己的项目的工作流,请查看Entity Framework Development Workflows.
开发工具:
vs2013
.NET4.5
EF6(有关ef的版本,根据项目安装版本而定)
创建MVC应用程序
设置网站风格
在Views\Shared\_Layout.cshtml模板页中做如下修改:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <meta charset="utf-8" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 7 <title>@ViewBag.Title - ContosoUniversity</title> 8 @Styles.Render("~/Content/css") 9 @Scripts.Render("~/bundles/modernizr") 10 </head> 11 <body> 12 <div class="navbar navbar-inverse navbar-fixed-top"> 13 <div class="container"> 14 <div class="navbar-header"> 15 <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> 16 <span class="icon-bar"></span> 17 <span class="icon-bar"></span> 18 <span class="icon-bar"></span> 19 </button> 20 @Html.ActionLink("Contosouniversity", "Index", "Home", null, new { @class = "navbar-brand" }) 21 </div> 22 <div class="navbar-collapse collapse"> 23 <ul class="nav navbar-nav"> 24 <li>@Html.ActionLink("主页", "Index", "Home")</li> 25 <li>@Html.ActionLink("关于", "About", "Home")</li> 26 <li>@Html.ActionLink("联系方式", "Contact", "Home")</li> 27 <li>@Html.ActionLink("Students", "Index", "Student")</li> 28 <li>@Html.ActionLink("Courses", "Index", "Course")</li> 29 <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li> 30 <li>@Html.ActionLink("Departments", "Index", "Department")</li> 31 </ul> 32 </div> 33 </div> 34 </div> 35 <div class="container body-content"> 36 @RenderBody() 37 <hr /> 38 <footer> 39 <p>© @DateTime.Now.Year - Contosouniversity</p> 40 </footer> 41 </div> 42 43 @Scripts.Render("~/bundles/jquery") 44 @Scripts.Render("~/bundles/bootstrap") 45 @RenderSection("scripts", required: false) 46 </body> 47 </html>
同时,在Views\Home\Index.cshtml中修改首页信息。
1 @{ 2 ViewBag.Title = "首页"; 3 } 4 <div class="jumbotron"> 5 <h1>Contoso University</h1> 6 </div> 7 <div class="row"> 8 <div class="col-md-4"> 9 <h2>Welcome to Contoso University</h2> 10 <p> 11 Contoso University is a sample application that 12 demonstrates how to use Entity Framework 6 in an 13 ASP.NET MVC 5 web application. 14 </p> 15 </div> 16 <div class="col-md-4"> 17 <h2>Build it from scratch</h2> 18 <p>You can build the application by following the steps in the tutorial series on the ASP.NET site.</p> 19 <p><a class="btn btn-default" href="http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/">See the tutorial »</a></p> 20 </div> 21 <div class="col-md-4"> 22 <h2>Download it</h2> 23 <p>You can download the completed project from the Microsoft Code Gallery.</p> 24 <p><a class="btn btn-default" href="http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8">Download »</a> 25 </p> 26 </div> 27 </div>
安装EF6
工具->库程序包管理器(NuGet程序包管理器)->程序包管理器控制台,打开控制台。
在控制台中输入:Install-Package EntityFramework。教材中安装的版本是6.0.0.这种安装方法是安装的最新版本的EF。可以看到我做的此项目中安装的是6.1.3
ok安装成功!
在Model文件夹中创建数据模型
我们将创建3个实体类,在Model文件夹中添加类文件:Student,Enrollment,Course。我们可以理解为:学生,成绩。课程三个实体。
看下三个实体类的对应关系:
Student实体:
1 using System; 2 using System.Collections.Generic; 3 4 namespace ContosoUniversity.Models 5 { 6 public class Student 7 { 8 public int ID { get; set; } 9 public string LastName { get; set; } 10 public string FirstMidName { get; set; } 11 public DateTime EnrollmentDate { get; set; } 12 public virtual ICollection<Enrollment> Enrollments { get; set; } 13 } 14 }
默认情况下,我们写的实体类中的属性名字为xxxID或者ID的,例如Student实体类中有个ID属性以及下面的Course实体会有一个CourseID属性,在EF中,这些属性会被自动解析成数据库中表的主键。
上面Student类中的,Enrollments是导航属性(navigation property),实体的导航属性能使其他实体(Enrollment实体)关联到该实体。Student和Enrollment实体是1:N关系,在以后做查询功能时候,我们就可以查出一个学生的所有成绩。导航属性通常为virtual
,这样可以充分利用EF的一些功能,例如懒加载(lazy loading),这个稍后教程会有涉及。此处不做讲解。
Enrollment实体:
1 namespace ContosoUniversity.Models 2 { 3 public class Enrollment 4 { 5 public int EnrollmentID { get; set; } 6 public int StudentID { get; set; } 7 public int CourseID { get; set; } 8 public Grade? Grade { get; set; } 9 public virtual Course Course { get; set; } 10 public virtual Student Student { get; set; } 11 } 12 public enum Grade 13 { 14 A, B, C, D, F 15 } 16 }
我们使用Grade枚举来表示学生成绩,注意Grade?此处表示成绩可以为空。EF会自动将CourseID和StudentID解析为数据库表中的外键。
Course实体:
1 using System.Collections.Generic; 2 using System.ComponentModel.DataAnnotations.Schema; 3 4 namespace ContosoUniversity.Models 5 { 6 public class Course 7 { 8 [DatabaseGenerated(DatabaseGeneratedOption.None)] 9 public int CourseID { get; set; } 10 public string Title { get; set; } 11 public int Credits { get; set; } 12 public virtual ICollection<Enrollment> Enrollments { get; set; } 13 } 14 }
我们要编辑Course表主键,表ID不需要数据库自动生成,所以使用DatabaseGenerated特性。DatabaseGenerated特性将会在教程后面做讲解。
创建数据上下文
数据库上下文类继承自:System.Data.Entity.DbContext。上下文类可以调用EF特有功能,在此之中。我们可以指定哪个实体被包含在数据模型中,同时还可以指定一些EF的行为。
项目中添加DAL文件夹,在DAL文件夹中添加SchoolContext类文件。代码如下
1 using ContosoUniversity.Models; 2 using System.Data.Entity; 3 using System.Data.Entity.ModelConfiguration.Conventions; 4 5 namespace ContosoUniversity.DAL 6 { 7 public class SchoolContext : DbContext 8 { 9 /// <summary> 10 /// 指定连接字符串,名称为SchoolContext 11 /// 如果此处不指定连接字符串名称。那么ef会默认将此类名称作为连接字符串名称,即SchoolContext 12 /// </summary> 13 public SchoolContext() 14 : base("SchoolContext") 15 { } 16 public DbSet<Student> Students { get; set; } 17 public DbSet<Course> Courses { get; set; } 18 public DbSet<Enrollment> Enrollments { get; set; } 19 /// <summary> 20 /// 指定单一表名称,如果不添加此方法,那么数据库中出现的表名称将会为复数(Students,Courses,Enrollments) 21 /// </summary> 22 /// <param name="modelBuilder"></param> 23 protected override void OnModelCreating(DbModelBuilder modelBuilder) 24 { 25 modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 26 } 27 } 28 }
指定实体集
在EF中,DbSet会对应数据中一张表,DbSet中的每个实体信息会对应表中的一行数据。
注意:在上述代码中,可以省略17行和18行。EF会自动包含它们,原因是Student实体引用了Enrollment实体,而Enrollment实体引用了Course实体
指定数连接字符串
此处指定了数据库连接字符串名称。在此处也可以为base(),这样EF会默认将数据库上下文的类名作为连接字符串名,即SchoolContext。
指定单数表名称
上述方法是用来指定生成的数据库表名称为单数,如果不写此方法,在数据库中的表名称将会:Students,Courses,Enrollments。
使用测试数据初始化数据库
程序运行的时候,EF会自动创建(或者是删除之后重建)数据库,可以指定EF是在程序运行的时候修改数据库还是当数据模型与现有数据库中的数据不同步的时候创建数据库。你也可以写个Seed方法,在EF创建完成数据库以后会自动调用该方法,并将测试数据写入新创建的数据库。
默认情况是,当数据库不存在的时候才会创建(如何数据库已存在并且数据模型发生改变,就会抛出异常),本节中,我们将做的是,如果数据实体型发生变化,将会删除并重建数据库。尽管删除数据库以后数据会丢失,但是没关系,因为我们写了一个Seed方法,此方法在数据库重建以后会重新写入数据。但是我们在实际生产中,并不希望这么做。所以。在稍后,将会讲解不使用这种删除重建的方法,而是当模型数据改变以后使用Code First Migrations技术修改数据库架构。
在DAL文件夹中创建SchoolInitailizer.cs文件,代码如下:
using System;using System.Collections.Generic;using System.Data.Entity;using ContosoUniversity.Models; namespace ContosoUniversity.DAL{ public class SchoolInitailizer:DropCreateDatabaseIfModelChanges<SchoolContext> { /// <summary> /// 每组实体后面的SaveChanges方法不是必须的,这么写是当数据写入数据库如果报错方便查找错误。 /// </summary> /// <param name="context"></param> 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{CourseID=1050,Title="Chemistry",Credits=3,}, new Course{CourseID=4022,Title="Microeconomics",Credits=3,}, new Course{CourseID=4041,Title="Macroeconomics",Credits=3,}, new Course{CourseID=1045,Title="Calculus",Credits=4,}, new Course{CourseID=3141,Title="Trigonometry",Credits=4,}, new Course{CourseID=2021,Title="Composition",Credits=3,}, new Course{CourseID=2042,Title="Literature",Credits=4,} }; courses.ForEach(c=>context.Courses.Add(c)); context.SaveChanges(); var enrollments = new List<Enrollment> { new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A}, new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C}, new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B}, new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B}, new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F}, new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F}, new Enrollment{StudentID=3,CourseID=1050}, new Enrollment{StudentID=4,CourseID=1050,}, new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F}, new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C}, new Enrollment{StudentID=6,CourseID=1045}, new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A} }; enrollments.ForEach(e=>context.Enrollments.Add(e)); context.SaveChanges(); } }
不必要在每个实体组后添加SaveChanges
方法,这里这么做是为了向数据库中写入数据时如果抛出异常,便于查看。
接下来,配置EF在网站根目录下的webconfig 文件中,指定初始化数据。
<entityFramework> <contexts> <context type="ContosoUniversity.DAL.SchoolContext"> <databaseInitializer type="ContosoUniversity.DAL.SchoolInitailizer"/> </context> </contexts>
如果你不想使用初始化数据,可以在Context节点中指定:disableDatabaseInitialization="true",更多有关EF的配置,请看,Entity Framework - Config File Settings.
也可以在Global.asax.cs文件中的Application_Start
方法中添加Database.SetInitializer
声明代替上述做法。详情请看:Understanding Database Initializers in Entity Framework Code First.
程序已经创建起来了,这样当你第一次运行程序的时候,EF会将你的数据模型和数据库做比较,如果二者存在差异,程序就会删除重建数据库。
注意:在实际部署项目的时候,一定要删除或者禁用删除-重建数据库这个功能。在后面的教程里我们将会讲解。
使用SQL SERVER EXPRESS LocalDB数据库设置EF(配置链接字符串)
注意:此步骤可以省略,因为EF会根据数据库上下文类默认使用连接字符串;更多信息请见:Code First to a New Database.
localdb是一个轻量级的SQL Server Express Database Engine,它易于安装和配置,以用户模式运行。localdb运行在SQL Server Express特殊的执行模式下:你可以操作.mdf文件来操作数据库。如果你想copy数据库,可以将Localdb数据库文件放在项目中的App_Data 文件夹中。尽管SQL Server Express中的用户实例特性可以让你操作.mdf文件,但是此做法在SQL Server Express不推荐,而在localdb中可以。在vs2012和其以后的版本,安装vs的同时localdb会被默认安装。
通常SQL Server Express不适用于实际开发中,并且localdb尤其不为推荐,因为他不能与IIS一起工作。
注意数据源的写法:本篇教程使用vs2013开发
如果你使用的vs2015,那么要将v11.0替换成MSSQLLocalDB
如果你想让数据库文件创建在项目中的App_Data文件夹中,可以在连接字符串中添加AttachDBFilename=|DataDirectory|\ContosoUniversity1.mdf
。更多详情请查看:SQL Server Connection Strings for ASP.NET Web Applications.。
创建学生控制器和视图
生成项目,添加控制器和视图
添加完成自带视图的控制器,运行项目,即可查看我们刚才添加的数据,这里就不贴图了。
查看数据库
我们可以使用vs中的server Explorer 或者 SQL Server Object Explore来查看生成的数据库。
1.关闭浏览器中的Index页面。
2.视图->服务器资源管理器->数据连接
3.在Student表上右击->显示表数据,结果报错了~~~~。
解决方法:
工具->扩展和更新
重启vs。这样就可以查看表数据了。
4.关闭数据库连接。
注意:如果Web.config中未添加连接字符串,那么在vs中会看不到添加的数据库数据库。如图:
这样,一个简单的应用程序就完成了。