MVC5使用EF6 Code First--创建EF数据模型(一)

此Web应用程序演示如何使用Entity Framework 6和Visual Studio 2015创建ASP.NET MVC 5应用程序。本教程使用“Code First ”即代码先行。有关如何在“Code First”,“Database First”和“Model First”之间进行选择,请参阅实体框架开发工作流程。如下:

  • Database First

    如果已经拥有数据库,Visual Studio中内置的Entity Framework设计器可以自动生成一个数据模型,该模型由对应于现有数据库对象(如表和列)的类和属性组成。有关数据库结构,数据模型及映射之间的信息以XML格式存储在.edmx文件中。实体框架设计器提供了一个可视化界面,您可以使用它来显示和编辑.edmx文件。

  • Model First

    如果您还没有数据库,则可以使用Visual Studio中的Entity Framework设计器在.edmx文件中创建一个模型。当模型建完后,可以执行.edmx文件来创建数据库。

  • Code First

    无论您是否拥有数据库,都可以使用Code First。如果没有数据库,可以编写类和对应于表和列的属性。如果有数据库,那么Entity Framework可以生成与现有表和列对应的类和属性。如果使用Code First创建数据库,则可以使用“migration(迁移)”来将数据库部署到生产环境。当数据模型更改时,可以将更改部署到生产环境中,而不改变原有的数据。

Contoso University Web应用程序

此教程中构建的应用程序是一个简单的web网站。
用户可以查看和更新学生课程和教师信息。以下是创建的几个页面。

创建一个MVC Web应用程序

打开Visual Studio 2015并创建一个名为“EFDemo”的新C# Web项目。

在“ 新建ASP.NET项目”对话框中,选择MVC模板。

更改身份验证,改为不进行身份验证(N)。

单击“ 确定”创建项目。

做几个简单的更改。打开视图\ 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 - Contoso University </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("Contoso University ", "Index", "Home", new { area = "" }, 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("学生", "Index", "Student")</li>
27                     <li>@Html.ActionLink("课程", "Index", "Course")</li>
28                     <li>@Html.ActionLink("老师", "Index", "Instructor")</li>
29                     <li>@Html.ActionLink("部门", "Index", "Department")</li>
30                 </ul>
31             </div>
32         </div>
33     </div>
34     <div class="container body-content">
35         @RenderBody()
36         <hr />
37         <footer>
38             <p>&copy; @DateTime.Now.Year - Contoso University </p>
39         </footer>
40     </div>
41 
42     @Scripts.Render("~/bundles/jquery")
43     @Scripts.Render("~/bundles/bootstrap")
44     @RenderSection("scripts", required: false)
45 </body>
46 </html>

Views \ Home \ Index.cshtml中,使用以下代码替换原有内容:

 1 @{
 2     ViewBag.Title = "Home Page";
 3 }
 4 
 5 <div class="jumbotron">
 6     <h1>Contoso University </h1>
 7 </div>
 8 <div class="row">
 9     <div class="col-md-4">
10         <h2>Welcome to Contoso University </h2>
11         <p>
12             Contoso University is a sample application that
13             demonstrates how to use Entity Framework 6 in an
14             ASP.NET MVC 5 web application.
15         </p>
16     </div>
17     <div class="col-md-4">
18         <h2>Build it from scratch</h2>
19         <p>You can build the application by following the steps in the tutorial series on the ASP.NET site.</p>
20         <p><a class="btn btn-default" href="http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/">See the tutorial &raquo;</a></p>
21     </div>
22     <div class="col-md-4">
23         <h2>Download it</h2>
24         <p>You can download the completed project from the Microsoft Code Gallery.</p>
25         <p><a class="btn btn-default" href="http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8">Download &raquo;</a></p>
26     </div>
27 </div>
\Home\Index.cshtml

CTRL + F5运行网站,显示如下:

安装EF6

工具菜单中单击NuGet包管理器,然后单击程序包管理器控制台

在“ 管理器管理器”窗口中,输入以下命令:

Install-Package EntityFramework

第二种方法:项目上右击选择管理NuGet程序包,选择浏览输入EntityFramework搜索,列表就会列出结果,选择要安装的包,点击右侧的安装(我已经安装过了,所以显示的是"卸载")

这里显示安装的是Entity Framework6.1.3,NuGet将安装最新版本的Entity Framework(不包括预发行版本)。

创建数据模型

接下来,您将为Contoso University 应用程序创建实体。将从以下三个实体开始:

StudentEnrollment是一对多关系CourseEnrollment也是一对多的关系换句话说,学生可以注册任何数量的课程,课程可以被任何数量的学生注册(学生和课程是多对多关系)。

接下来将为每个实体创建一个类。

Student

Models文件夹中,创建一个名为Student.cs的类文件,并使用以下代码替换模板代码:

 1 using System;
 2 using System.Collections.Generic;
 3 
 4 namespace EFDemo.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         
13         public virtual ICollection<Enrollment> Enrollments { get; set; }
14     }
15 }
Student.cs

ID属性将成为数据库表的主键列。默认情况下,Entity Framework将一个名为IDclassnameID的属性作为主键。

该 Enrollments属性是导航属性导航属性包含与该实体相关的其他实体信息。在这种情况下,Enrollments持有与该Student实体相关的Enrollment实体的所有信息。

导航属性通常被定义为virtual使得它们可以利用某些实体框架功能,例如延迟加载

如果导航属性包含多个实体(如多对多或一对多关系),则其类型必须是list集合,例如ICollection

Enrollment

Models文件夹中,创建Enrollment.cs并使用以下代码替换现有代码:

 1 namespace EFDemo.Models
 2 {
 3     public enum Grade
 4     {
 5         A, B, C, D, F
 6     }
 7 
 8     public class Enrollment
 9     {
10         public int EnrollmentID { get; set; }
11         public int CourseID { get; set; }
12         public int StudentID { get; set; }
13         public Grade? Grade { get; set; }
14         
15         public virtual Course Course { get; set; }
16         public virtual Student Student { get; set; }
17     }
18 }
Enrollment.cs

该EnrollmentID属性是主键; 该实体主键使用类名 加ID的命名方式,而不是直接使用ID通常会选择一种固定的命名方式,并在所有数据模型中使用。在这里,可以使用任意命名方式。在后面的教程中,将看到如何使用ID,而不用classnameID,这样更容易在数据模型中实现继承。

Grade属性是一个枚举类型Grade类型后的问号表示该Grade属性可以为空空值表示未知等级或尚未分配。

StudentID属性是一个外键,以及相应的导航属性Student一个Enrollment实体与一个Student实体相关联,因此该属性只能保存一个Student实体

CourseID属性也是一个外键,以及相应的导航属性Course一个Enrollment实体与一个Course实体相关联

Course

Models文件夹中,创建Course.cs,使用以下代码替换模板代码: 

 1 using System.Collections.Generic;
 2 using System.ComponentModel.DataAnnotations.Schema;
 3 
 4 namespace EFDemo.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         
13         public virtual ICollection<Enrollment> Enrollments { get; set; }
14     }
15 }
Course.cs

Enrollments属性是导航属性。一个Course实体可以有任意数量的Enrollment实体。

DatabaseGenerated属性,DatabaseGeneratedOption.None 设置允许输入课程主键,而不是让数据库自动生成它(不是自动增长)。

创建数据库上下文

右键单击该项目解决方案资源管理器,然后单击新建文件夹。命名新文件夹DAL(用于数据访问层)。在该文件夹中创建一个名为SchoolContext.cs的类,代码如下:

 1 using EFDemo.Models;
 2 using System.Data.Entity;
 3 using System.Data.Entity.ModelConfiguration.Conventions;
 4 
 5 namespace EFDemo.DAL
 6 {
 7     public class SchoolContext : DbContext
 8     {
 9         public SchoolContext() : base("SchoolDbContext")
10         {
11 
12         }
13 
14         public DbSet<Student> Students { get; set; }
15         public DbSet<Enrollment> Enrollments { get; set; }
16         public DbSet<Course> Courses { get; set; }
17 
18         protected override void OnModelCreating(DbModelBuilder modelBuilder)
19         {
20             //阻止表名复数形式
21             modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
22         }
23     }
24 }
SchoolContext.cs

指定连接字符串

连接字符串的名称传递给构造函数

1 public SchoolContext() : base("SchoolDbContext")
2 {
3 }

Web.config文件中连接字符串如以下所示:

1  <connectionStrings>
2        <add name="SchoolDbContext" providerName="System.Data.SqlClient" connectionString="Data Source=ServerName;Initial Catalog=DatabaseName;Integrated Security=False;User Id=userid;Password=password;MultipleActiveResultSets=True" />
3    </connectionStrings>

也可以传递连接字符串本身,而不是存储在Web.config文件中。

如果不指定连接字符串或显式指定名称,则Entity Framework假定连接字符串名称与类名称相同。

指定实体集DbSet<>

每个实体集创建一个DbSet属性。在EF中,实体集对应于数据库的表,实体对应于表中的一行。

注意:可以省略声明DbSet<Enrollment>DbSet<Course>,它们将正常工作。EF将默认隐式地包含它们。

指定单数表名

OnModelCreating 方法中modelBuilder.Conventions.Remove语句阻止表名的复数形式。如果不这样做,在数据库中生成的表将被命名为复数形式(Students)。

设置使用测试数据来初始化数据库

EF可以在应用程序运行时(或者当模型与现有数据库不同步时)自动创建(或删除或重新创建)数据库。还可以编写一个 Seed 方法,EF将在创建数据库后自动调用,以便添加测试数据。

EF默认的是在数据库不存在(CreateDatabaseIfNotExists)时创建数据库(如果模型已更改,数据库已经存在),则抛出异常。在本章中,将指定每当模型改变时,才删除和重新创建数据库。删除和重新创建数据库会丢失数据库中原先的所有数据。这通常只在开发过程中使用,因为Seed方法将在数据库重新创建时才运行,并重新添加测试数据。但在生产中环境,不可能每次更改数据库时丢失所有数据。在下面的章节,将学习如何通过使用Code First  migration(迁移)来更改数据库,而不影响原来的数据。


 CreateDatabaseIfNotExists

这是默认的数据库初始化类,除非手动指定其他类。顾名思义,CreateDatabaseIfNotExists类仅在不存在数据库时创建数据库。

DropCreateDatabaseIfModelChanges

只要当模型类和表之间不匹配时,就会删除数据库并重新创建它。

DropCreateDatabaseAlways

每次运行应用程序时,都将删除并重新创建数据库,而不管它是否已经存在。


 在DAL文件夹中,创建一个名为SchoolInitializer.cs的类,代码如下:

 1 using EFDemo.Models;
 2 using System;
 3 using System.Collections.Generic;
 4 using System.Data.Entity;
 5 
 6 namespace EFDemo.DAL
 7 {
 8     public class SchoolInitializer : DropCreateDatabaseIfModelChanges<SchoolContext>
 9     {
10         protected override void Seed(SchoolContext context)
11         {
12             var students = new List<Student>
13             {
14             new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
15             new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
16             new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
17             new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
18             new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
19             new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
20             new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
21             new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
22             };
23             students.ForEach(s => context.Students.Add(s));
24             context.SaveChanges();
25 
26             var courses = new List<Course>
27             {
28             new Course{CourseID=1050,Title="Chemistry",Credits=3,},
29             new Course{CourseID=4022,Title="Microeconomics",Credits=3,},
30             new Course{CourseID=4041,Title="Macroeconomics",Credits=3,},
31             new Course{CourseID=1045,Title="Calculus",Credits=4,},
32             new Course{CourseID=3141,Title="Trigonometry",Credits=4,},
33             new Course{CourseID=2021,Title="Composition",Credits=3,},
34             new Course{CourseID=2042,Title="Literature",Credits=4,}
35             };
36             courses.ForEach(s => context.Courses.Add(s));
37             context.SaveChanges();
38 
39             var enrollments = new List<Enrollment>
40             {
41             new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
42             new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
43             new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
44             new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
45             new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
46             new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
47             new Enrollment{StudentID=3,CourseID=1050},
48             new Enrollment{StudentID=4,CourseID=1050,},
49             new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
50             new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
51             new Enrollment{StudentID=6,CourseID=1045},
52             new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
53             };
54             enrollments.ForEach(s => context.Enrollments.Add(s));
55             context.SaveChanges();
56         }
57     }
58 }
SchoolInitializer.cs

该Seed方法用于添加测试数据。

指定数据库初始化程序

打开Web.config文件,添加代码如下: 

 1 <entityFramework>
 2 
 3     <contexts>
 4       <context type="EFDemo.DAL.SchoolContext, EFDemo">
 5         <databaseInitializer type="EFDemo.DAL.SchoolInitializer, EFDemo" />
 6       </context>
 7     </contexts>
 8 
 9     <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
10       <parameters>
11         <parameter value="mssqllocaldb" />
12       </parameters>
13     </defaultConnectionFactory>
14     <providers>
15       <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
16     </providers>
17   </entityFramework>

或者

1   <appSettings>
2     <add key = "DatabaseInitializerForType EFDemo.DAL.SchoolContext,EFDemo" value = "EFDemo.DAL.SchoolInitializer,EFDemo" />
3   </appSettings>

它必须以预定义值DatabaseInitializerForType开头,后跟一个空格,然后是上下文类的全名称。

context节点,type指定上下文类的全名称,后面是程序集的名称。当你不想EF使用初始化,您可以给context元素设置一个属性disableDatabaseInitialization="true"

作为在Web.config文件中设置初始化程序的替代方法是在Global.asax.cs文件中通过执行代码来设置:

 1  protected void Application_Start()
 2  {
 3    AreaRegistration.RegisterAllAreas();
 4    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
 5    RouteConfig.RegisterRoutes(RouteTable.Routes);
 6    BundleConfig.RegisterBundles(BundleTable.Bundles);
 7 
 8    //指定数据库初始化程序
 9    Database.SetInitializer(new SchoolInitializer());
10  }

SetInitializer() 方法接受数据库初始化程序类的实例,并将其设置为当前应用程序数据库初始化程序。设置数据库初始化程序时,不会立即调用。当第一次使用上下文(SchoolContext)时,它将被调用。

当首次访问数据库时,Entity Framework会将数据库与模型进行比较,如果有差异,将删除并重新创建数据库。

如果在数据库初始化时,不想使用延迟加载,可以使用Initialize()方法覆盖。如果模型复杂且初始化需要很多时间,可以显式调用数据库初始化程序。这样就可以在某个已知步骤执行初始化(并且可以向用户显示等待消息)。使用Initialize()方法,代码如下所示:

1  Database.Initialize(false);//立刻初始化数据,不延迟加载

在上述代码中,数据库将在调用Initialize()方法后立即创建,而不是延迟加载。Initialize()方法接受一个布尔类型的参数,用于控制是否重新执行初始化。指定false,如果已经执行,则跳过初始化过程。指定true,将重新初始化,即使它已被初始化。

有时可能不希望执行数据库初始化逻辑。在这种情况下,可以通过将null传递给SetInitializer()方法,则将跳过初始化。

1 Database.SetInitializer<SchoolContext>(null);

注意:如果将应用程序部署到生产环境,则必须删除或禁用初始化代码。

创建自定义数据库初始化程序

在上述示例中,使用内置的数据库初始化程序。还可以通过实现IDatabaseInitializer<>接口来创建自定义数据库初始化程序。需要实现IDatabaseInitializer<>接口的InitializeDatabase()方法,并编写自己的数据库创建逻辑。以下代码显示了InitializeDatabase()方法的示例实现:

 1 public class SchoolInitializer : IDatabaseInitializer<SchoolContext>
 2     {
 3         #region 继承 IDatabaseInitializer<SchoolContext>接口,实现InitializeDatabase()方法
 4         public void InitializeDatabase(SchoolContext context)
 5         {
 6             //判断数据库是否已经存在
 7             if (context.Database.Exists())
 8             {
 9                 //数据库模式是否与模型兼容
10                 if (!context.Database.CompatibleWithModel(true))
11                 {
12                     context.Database.Delete();
13                 }
14             }
15             context.Database.Create();
16             //使用自定义初始值设置来执行自定义任务
17             context.Database.ExecuteSqlCommand("CREATE TABLE CS_DATA([Id] int identity(1,1) primary key, [Name] NVARCHAR(50) not null)");
18         }
19         #endregion
20     }

注意:代码使用ExecuteSqlCommand()方法来创建不属于模型的表。虽然我们在示例中不使用该表,但它说明了如何使用自定义初始化设置来执行自定义任务。

创建Student控制器和视图

现在,将创建一个页面来显示数据,请求数据的过程将自动触发数据库的创建。先创建一个新的控制器。但是在这之前,需要重新生成整个项目,因为我们将使用MVC脚手架自动生成CRUD页面。

  1. 右键单击解决方案资源管理器中Controllers文件夹,选择添加,然后单击控制器
  2. 在弹出的对话框中,选择包含视图的MVC 5控制器(使用Entity Framework)

  3. 在“添加控制器”对话框中,进行以下选择,然后单击“ 添加”

    • 模特类:学生(Student(EFDemo.Models))(如果在下拉列表中没有看到此选项,则重新生成项目,然后重试)
    • 数据上下文类:SchoolContext(EFDemo.DAL)
    • 控制器名称:StudentController
    • 其他保留默认值。

 

当单击添加时,将创建一个StudentController.cs文件和一组与控制器配合使用的视图(.cshtml文件)。

4.Visual Studio打开Controllers \ StudentController.cs文件。您看到已经创建了一个实例化数据库上下文对象的类变量:

1  private SchoolContext db = new SchoolContext();

Index操作方法通过数据库上下文实例的属性查询出所有学生信息:

1   // GET: Student
2   public ActionResult Index()
3   {
4     return View(db.Students.ToList());
5   }

学生\ Index.cshtml视图显示此列表中的表:

 1 @model IEnumerable<EFDemo.Models.Student>
 2 
 3 @{
 4     ViewBag.Title = "Index";
 5 }
 6 
 7 <h2>Index</h2>
 8 
 9 <p>
10     @Html.ActionLink("Create New", "Create")
11 </p>
12 <table class="table">
13     <tr>
14         <th>
15             @Html.DisplayNameFor(model => model.LastName)
16         </th>
17         <th>
18             @Html.DisplayNameFor(model => model.FirstMidName)
19         </th>
20         <th>
21             @Html.DisplayNameFor(model => model.EnrollmentDate)
22         </th>
23         <th></th>
24     </tr>
25 
26 @foreach (var item in Model) {
27     <tr>
28         <td>
29             @Html.DisplayFor(modelItem => item.LastName)
30         </td>
31         <td>
32             @Html.DisplayFor(modelItem => item.FirstMidName)
33         </td>
34         <td>
35             @Html.DisplayFor(modelItem => item.EnrollmentDate)
36         </td>
37         <td>
38             @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
39             @Html.ActionLink("Details", "Details", new { id=item.ID }) |
40             @Html.ActionLink("Delete", "Delete", new { id=item.ID })
41         </td>
42     </tr>
43 }
44 
45 </table>
View Code

5.按CTRL + F5运行项目。(或Index.cshtml文件右击在浏览器中查看或者直接按F5)

单击“ 学生”选项卡以查看Seed()方法插入的测试数据

查看数据库

当您运行学生页面,应用程序尝试访问数据库时,EF看到没有数据库,因此它创建了一个数据库,然后运行Seed()方法添加测试数据。

因为正在使用DropCreateDatabaseIfModelChanges初始化程序,现在可以对Student进行更改,再次运行应用程序,将自动重新创建数据库。例如,如果Student该类添加一个属性EmailAddress,再次运行“学生”页面,然后再次查看该表,将看到一个新列EmailAddress

1   public string EmailAddress { get; set; }

当重新运行时,可能会报这个错:

解决办法:先删除原数据库,勾选关闭现有连接,然后再运行程序。后面利用迁移就不需要手动删除数据库了。

然后查看数据库,增加了新的列

约定

使用Entity Framework能够创建一个完整的数据库,发现我们只编写了很少的代码,因为Entity Framework有默认约定

  • 实体类名一般用作表名。
  • 实体属性名称用于列名。
  • 名称ID或classnameID属性被识别为主键属性。
  • 还有外键属性,例如,StudentID用于Student导航属性

当然我们可以将属性显式地标记为外键属性等。在本系列后面继续学习。

参考:https://docs.microsoft.com/zh-cn/aspnet/

猜你喜欢

转载自www.cnblogs.com/vanblog/p/7543793.html