为 ASP.NET MVC 应用程序创建实体框架数据模型 (10 个)

作者 :Tom Dykstra

注意

本教程系列的较新版本适用于 Visual Studio 2013、Entity Framework 6 和 MVC 5。

Contoso University 示例 Web 应用程序演示如何使用 Entity Framework 5 和 Visual Studio 2012 创建 ASP.NET MVC 4 应用程序。 示例应用程序供一个虚构的 Contoso 大学网站使用。 它包括诸如学生入学、 课程创建和导师分配等功能。 本教程系列介绍如何生成 Contoso University 示例应用程序。

Code First

可通过三种方式处理实体框架中的数据: 数据库优先模型优先代码优先。 本教程适用于 Code First。 有关这些工作流之间的差异以及如何为方案选择最佳工作流的指南的信息,请参阅 实体框架开发工作流

MVC

示例应用程序基于 ASP.NET MVC 构建。 如果希望使用 ASP.NET Web Forms模型,请参阅模型绑定和Web Forms教程系列和 ASP.NET 数据访问内容映射

软件版本

显示在教程中 还适用于
Windows 8 Windows 7
Visual Studio 2012 Visual Studio 2012 Express for Web。 如果还没有 VS 2012 或 VS 2012 Express for Web,则 Windows Azure SDK 会自动安装此功能。 Visual Studio 2013应该可以运行,但本教程尚未使用它进行测试,并且某些菜单选择和对话框是不同的。 Windows Azure SDK 的 VS 2013 版本是 Windows Azure 部署所必需的。
.NET 4.5 显示的大部分功能在 .NET 4 中都正常工作,但有些功能不起作用。 例如,EF 中的枚举支持需要 .NET 4.5。
实体框架 5
Windows Azure SDK 2.1 如果跳过 Windows Azure 部署步骤,则不需要 SDK。 发布新版本的 SDK 时,链接将安装较新版本。 在这种情况下,可能需要根据新的 UI 和功能调整一些说明。

问题

如果有与本教程不直接相关的问题,可以发布到 ASP.NET 实体框架论坛实体框架和 LINQ to Entities 论坛StackOverflow.com

致谢

有关 确认和有关 VB 的说明,请参阅系列中的最后一个教程。

Contoso University Web 应用程序

你将在这些教程中学习构建一个简单的大学网站的应用程序。

用户可以查看和更新学生、 课程和教师信息。 以下是一些你即将创建的页面。

Students_Index_page

显示示例 Contoso University Web 应用程序的“学生搜索”页和“创建新学生”页的屏幕截图。

本教程主要关注于如何使用 Entity Framework , 所以此站点的UI样式都是直接套用内置的模板。

先决条件

本教程中的说明和屏幕截图假定你使用的是 Visual Studio 2012Visual Studio 2012 Express for Web,并且自 2013 年 7 月起安装了最新更新和 Azure SDK for .NET。 可以通过以下链接获取所有这些内容:

适用于 .NET 的 Azure SDK (Visual Studio 2012)

如果已安装 Visual Studio,则上面的链接将安装任何缺少的组件。 如果没有 Visual Studio,该链接将安装 Visual Studio 2012 Express for Web。 可以使用Visual Studio 2013,但某些必需的过程和屏幕会有所不同。

创建 MVC Web 应用程序

打开 Visual Studio 并使用 ASP.NET MVC 4 Web 应用程序 模板创建名为“ContosoUniversity”的新 C# 项目。 请确保目标.NET Framework 4.5 (将使用enum属性,这需要 .NET 4.5) 。

New_project_dialog_box

在“ 新建 ASP.NET MVC 4 项目 ”对话框中,选择 “Internet 应用程序 ”模板。

使 Razor 视图引擎保持选中状态,并清除“创建单元测试项目检查框。

单击 “确定”

Project_template_options

设置网站样式

通过几个简单的更改设置站点菜单、 布局和主页。

打开 Views\Shared\_Layout.cshtml,并将文件的内容替换为以下代码。 突出显示所作更改。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - Contoso University</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header>
            <div class="content-wrapper">
                <div class="float-left">
                    <p class="site-title">@Html.ActionLink("Contoso University", "Index", "Home")</p>
                </div>
                <div class="float-right">
                    <section id="login">
                        @Html.Partial("_LoginPartial")
                    </section>
                    <nav>
                        <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>
                    </nav>
                </div>
            </div>
        </header>
        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>
        <footer>
            <div class="content-wrapper">
                <div class="float-left">
                    <p>&copy; @DateTime.Now.Year - Contoso University</p>
                </div>
            </div>
        </footer>

        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)
    </body>
</html>

此代码会更改以下内容:

  • 将“我的 ASP.NET MVC 应用程序”和“此处的徽标”的模板实例替换为“Contoso University”。
  • 添加几个操作链接,这些链接将在本教程的后面部分使用。

Views\Home\Index.cshtml 中,将文件的内容替换为以下代码,以消除有关 ASP.NET 和 MVC 的模板段落:

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
        </div>
    </section>
}

Controllers\HomeController.cs 中,将 Action 方法中的 IndexViewBag.Message更改为“欢迎使用 Contoso University!”,如以下示例所示:

public ActionResult Index()
{
    ViewBag.Message = "Welcome to Contoso University";

    return View();
}

按 Ctrl+F5 运行站点。 会看到包含“main”菜单的主页。

Contoso_University_home_page

创建数据模型

接下来你将创建 Contoso 大学应用程序的实体类。 你将从以下三个实体开始:

Class_diagram

StudentEnrollment实体之间是一对多的关系,CourseEnrollment 实体之间也是一个对多的关系。 换而言之,一名学生可以修读任意数量的课程, 并且某一课程可以被任意数量的学生修读。

接下来,你将创建与这些实体对应的类。

注意

如果在完成所有这些实体类的创建之前尝试编译项目,则会收到编译器错误。

学生实体

Student_entity

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 属性将成为对应于此类的数据库表中的主键。 默认情况下,实体框架将名为 IDclassnameID 的属性解释为主键。

Enrollments 属性是导航属性。 导航属性中包含与此实体相关的其他实体。 在这种情况下,Enrollments实体的 Student 属性将保存与该EnrollmentStudent实体相关的所有实体。 换句话说,如果数据库中的给定 Student 行具有两个相关 Enrollment 行, (在其外键列中包含该学生的主键值的 StudentID 行) ,则该 Student 实体的 Enrollments 导航属性将包含这两个 Enrollment 实体。

导航属性通常定义为 virtual ,以便它们可以利用某些实体框架功能,例如 延迟加载。 (延迟加载将在稍后的 阅读相关数据 教程中介绍。

如果导航属性可以具有多个实体 (如多对多或一对多关系),那么导航属性的类型必须是可以添加、 删除和更新条目的容器,如 ICollection

注册实体

Enrollment_entity

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

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }
        
        public virtual Course Course { get; set; }
        public virtual Student Student { get; set; }
    }
}

Grade 属性是 枚举Grade 声明类型后的`?`表示 Grade 属性可以为 null。 为 null 的成绩不同于零等级 , null 表示尚未知道或尚未分配某个等级。

StudentID 属性是一个外键,Student 是与其且对应的导航属性。 Enrollment 实体与一个 Student 实体相关联,因此该属性只包含单个 Student 实体 (与前面所看到的 Student.Enrollments 导航属性不同后,`Student`中可以容纳多个 Enrollment 实体)。

CourseID 属性是一个外键, Course 是与其对应的导航属性。 Enrollment 实体与一个 Course 实体相关联。

Course 实体

Course_entity

Models 文件夹中,创建 Course.cs,并将现有代码替换为以下代码:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        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 实体相关。

我们将详细介绍 [DatabaseGenerated (DatabaseGeneratedOption。下一教程中无) ] 属性。 简单来说,此特性让你能自行指定主键,而不是让数据库自动指定主键。

创建数据库上下文

协调给定数据模型的实体框架功能的main类是数据库上下文类。 通过派生自 System.Data.Entity.DbContext 类来创建此类。 在该类中你可以指定数据模型中包含哪些实体。 你还可以定义某些 Entity Framework 行为。 在此项目中将数据库上下文类命名为 SchoolContext

为数据访问层) 创建名为 DAL (的文件夹。 在该文件夹中,创建名为 SchoolContext.cs 的新类文件,并将现有代码替换为以下代码:

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

namespace ContosoUniversity.DAL
{
    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 术语中, 实体集 通常对应于数据库表, 实体 对应于表中的行。

modelBuilder.Conventions.RemoveOnModelCreating 方法中的 语句阻止对表名进行复数化。 如果不这样做,生成的表将命名为 StudentsCoursesEnrollments。 相反,表名称将为 StudentCourseEnrollment。 开发者对表名称是否应为复数意见不一。 本教程使用单数形式,但重要的是,你可以通过包括或省略此代码行来选择你喜欢的窗体。

SQL Server Express LocalDB

LocalDB 是 SQL Server Express 数据库引擎的轻型版本,可按需启动并在用户模式下运行。 LocalDB 在SQL Server Express的特殊执行模式下运行,使你能够将数据库作为.mdf文件使用。 通常,LocalDB 数据库文件保存在 Web 项目的 App_Data 文件夹中。 SQL Server Express 中的用户实例功能也使你能够处理.mdf文件,但用户实例功能已弃用;因此,建议使用 LocalDB 来处理.mdf文件。

通常,SQL Server Express不用于生产 Web 应用程序。 特别是不建议将 LocalDB 用于 Web 应用程序的生产用途,因为它不是设计为与 IIS 配合使用。

在 Visual Studio 2012 及更高版本中,LocalDB 默认随 Visual Studio 一起安装。 在 Visual Studio 2010 及更早版本中,Visual Studio 默认安装没有 LocalDB) SQL Server Express (;如果使用 Visual Studio 2010,则必须手动安装它。

在本教程中,你将使用 LocalDB,以便可以将数据库作为 .mdf 文件存储在 App_Data 文件夹中。 打开根 Web.config 文件,向集合添加新连接字符串connectionStrings,如以下示例所示。 (请确保更新根项目文件夹中的 Web.config 文件。视图子文件夹中还有一个无需更新的Web.config 文件。)

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient" />

默认情况下,实体框架会查找与此项目) 的类 (SchoolContext 相同的DbContext连接字符串。 添加连接字符串指定位于 App_Data 文件夹中的名为 ContosoUniversity.mdf LocalDB 数据库。 有关详细信息,请参阅 SQL Server ASP.NET Web 应用程序的连接字符串

实际上不需要指定连接字符串。 如果不提供连接字符串,Entity Framework 将为你创建一个;但是,数据库可能不在应用的 App_data 文件夹中。 有关将在何处创建数据库的信息,请参阅 Code First to a New Database

集合connectionStrings还有一个名为 DefaultConnection 的连接字符串,用于成员资格数据库。 本教程中不会使用成员资格数据库。 这两个连接字符串之间的唯一区别是数据库名称和 name 属性值。

设置和执行代码优先迁移

首次开始开发应用程序时,数据模型会频繁更改,每次模型更改时,数据模型都会与数据库不同步。 可以将 Entity Framework 配置为在每次更改数据模型时自动删除并重新创建数据库。 这不是开发早期的问题,因为测试数据很容易重新创建,但在部署到生产环境后,通常需要在不删除数据库的情况下更新数据库架构。 迁移功能使 Code First 无需删除并重新创建数据库即可更新数据库。 在新项目开发周期的早期,你可能希望每次模型更改时都使用 DropCreateDatabaseIfModelChanges 删除、重新创建和重新设定数据库种子。 准备好部署应用程序后,可以转换为迁移方法。 在本教程中,你将仅使用迁移。 有关详细信息,请参阅Code First 迁移迁移截屏视频系列

启用 Code First 迁移

  1. 在“工具”菜单中,单击“NuGet 包管理器”,然后单击“包管理器控制台”

    Selecting_Package_Manager_Console

  2. 在提示符处 PM> 输入以下命令:

    enable-migrations -contexttypename SchoolContext
    

    enable-migrations 命令

    此命令在 ContosoUniversity 项目中创建 一个 Migrations 文件夹,并在该文件夹中放入一个可以编辑以配置迁移 的 Configuration.cs 文件。

    迁移文件夹

    Configuration 包括一个 Seed 方法,该方法在创建数据库时以及每次在数据模型更改后更新时调用。

    internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.Models.SchoolContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }
    
        protected override void Seed(ContosoUniversity.Models.SchoolContext context)
        {
            //  This method will be called after migrating to the latest version.
    
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
    

    此方法 Seed 的目的是在 Code First 创建数据库或更新数据库后,能够将测试数据插入数据库。

设置 Seed 方法

Seed 方法在Code First 迁移创建数据库时运行,每次将数据库更新到最新迁移时运行。 Seed 方法的目的是使你能够在应用程序首次访问数据库之前将数据插入表。

在早期版本的 Code First 中,在迁移发布之前,插入测试数据的方法很常见 Seed ,因为在开发过程中,每次模型更改都必须从头开始完全删除并重新创建数据库。 使用Code First 迁移,测试数据在数据库更改后保留,因此通常不需要在 Seed 方法中包含测试数据。 事实上,如果要使用迁移将数据库部署到生产环境,则不希望 Seed 方法插入测试数据,因为 Seed 该方法将在生产环境中运行。 在这种情况下,你希望 Seed 方法仅将要在生产环境中插入的数据插入到数据库中。 例如,当应用程序在生产环境中可用时,你可能希望数据库在 Department 表中包括实际的部门名称。

在本教程中,你将使用迁移进行部署,但你的 Seed 方法无论如何都会插入测试数据,以便更轻松地查看应用程序功能的工作原理,而无需手动插入大量数据。

  1. Configuration.cs 文件的内容替换为以下代码,这些代码会将测试数据加载到新数据库中。

    namespace ContosoUniversity.Migrations
    {
       using System;
       using System.Collections.Generic;
       using System.Data.Entity.Migrations;
       using System.Linq;
       using ContosoUniversity.Models;
    
       internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
       {
          public Configuration()
          {
             AutomaticMigrationsEnabled = false;
          }
    
          protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
          {
             var students = new List<Student>
                {
                    new Student { FirstMidName = "Carson",   LastName = "Alexander", 
                        EnrollmentDate = DateTime.Parse("2010-09-01") },
                    new Student { FirstMidName = "Meredith", LastName = "Alonso",    
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Arturo",   LastName = "Anand",     
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Gytis",    LastName = "Barzdukas", 
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Yan",      LastName = "Li",        
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Peggy",    LastName = "Justice",   
                        EnrollmentDate = DateTime.Parse("2011-09-01") },
                    new Student { FirstMidName = "Laura",    LastName = "Norman",    
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Nino",     LastName = "Olivetto",  
                        EnrollmentDate = DateTime.Parse("2005-08-11") }
                };
             students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, 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(s => context.Courses.AddOrUpdate(p => p.Title, s));
             context.SaveChanges();
    
             var enrollments = new List<Enrollment>
                {
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID, 
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID, 
                        Grade = Grade.A 
                    },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID, 
                        Grade = Grade.C 
                     },                            
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID, 
                        Grade = Grade.B
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
                        Grade = Grade.B         
                     },
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Li").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Justice").StudentID,
                        CourseID = courses.Single(c => c.Title == "Literature").CourseID,
                        Grade = Grade.B         
                     }
                };
    
             foreach (Enrollment e in enrollments)
             {
                var enrollmentInDataBase = context.Enrollments.Where(
                    s =>
                         s.Student.StudentID == e.StudentID &&
                         s.Course.CourseID == e.CourseID).SingleOrDefault();
                if (enrollmentInDataBase == null)
                {
                   context.Enrollments.Add(e);
                }
             }
             context.SaveChanges();
          }
       }
    }
    

    Seed 方法将数据库上下文对象作为输入参数,方法中的代码使用该对象向数据库添加新实体。 对于每种实体类型,代码会创建一个新实体的集合,将它们添加到相应的 DbSet 属性,然后将更改保存到数据库。 不必像此处那样在每组实体之后调用 SaveChanges 方法,但如果代码写入数据库时发生异常,则这样做有助于找到问题的根源。

    插入数据的一些语句使用 AddOrUpdate 方法执行“upsert”操作。 Seed由于 方法在每次迁移时都运行,因此不能只插入数据,因为尝试添加的行在创建数据库的第一次迁移之后已经存在。 “upsert”操作可防止在尝试插入已存在的行时发生的错误,但它 会替代 在测试应用程序时对数据所做的任何更改。 对于某些表中的测试数据,你可能不希望发生这种情况:在某些情况下,在测试时更改数据时,希望更改在数据库更新后保留。 在这种情况下,需要执行条件插入操作:仅当行尚不存在时才插入该行。 Seed 方法使用这两种方法。

    传递给 AddOrUpdate 方法的第一个参数指定要用于检查行(如果已存在)的属性。 对于你提供的测试学生数据,属性可用于此目的, LastName 因为列表中的每个姓氏都是唯一的:

    context.Students.AddOrUpdate(p => p.LastName, s)
    

    此代码假定姓氏是唯一的。 如果手动添加姓氏重复的学生,下次执行迁移时会出现以下异常。

    序列包含多个元素

    有关该方法的详细信息 AddOrUpdate ,请参阅 Julie Lerman 博客上的“ 小心使用 EF 4.3 AddOrUpdate 方法 ”。

    添加 Enrollment 实体的代码不使用 AddOrUpdate 方法。 它检查实体是否已存在,如果实体不存在,则插入该实体。 此方法将保留迁移运行时对注册等级所做的更改。 代码循环访问 List 的每个成员Enrollment,如果在数据库中找不到注册,则会将注册添加到数据库。 首次更新数据库时,数据库将为空,因此会添加每个注册。

    foreach (Enrollment e in enrollments)
    {
        var enrollmentInDataBase = context.Enrollments.Where(
            s => s.Student.StudentID == e.Student.StudentID &&
                 s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
        if (enrollmentInDataBase == null)
        {
            context.Enrollments.Add(e);
        }
    }
    

    有关如何调试 Seed 方法以及如何处理冗余数据(如两个名为“Alexander Carson”的学生)的信息,请参阅 Rick Anderson 博客上的 “EF) 数据库” (种子设定和调试实体框架

  2. 生成项目。

创建和执行第一次迁移

  1. 在“包管理器控制台”窗口中,输入以下命令:

    add-migration InitialCreate
    update-database
    

    显示“包管理器控制台”窗口的屏幕截图。突出显示了“添加连字符迁移下划线”“初始创建”和“更新连字符数据库”命令。

    命令 add-migration 向 Migrations 文件夹添加 一个 [DateStamp]_InitialCreate.cs 文件,其中包含用于创建数据库的代码。 第一个参数 (InitialCreate) 用于文件名,可以是所需的任何参数;通常选择汇总迁移中正在执行的操作的单词或短语。 例如,可将后面的迁移命名为“AddDepartmentTable”。

    初始迁移的迁移文件夹

    Up类的 InitialCreate 方法创建与数据模型实体集对应的数据库表,方法Down删除它们。 迁移调用 Up 方法为迁移实现数据模型更改。 输入用于回退更新的命令时,迁移调用 Down 方法。 以下代码显示文件的内容 InitialCreate

    namespace ContosoUniversity.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;
        
        public partial class InitialCreate : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Student",
                    c => new
                        {
                            StudentID = c.Int(nullable: false, identity: true),
                            LastName = c.String(),
                            FirstMidName = c.String(),
                            EnrollmentDate = c.DateTime(nullable: false),
                        })
                    .PrimaryKey(t => t.StudentID);
                
                CreateTable(
                    "dbo.Enrollment",
                    c => new
                        {
                            EnrollmentID = c.Int(nullable: false, identity: true),
                            CourseID = c.Int(nullable: false),
                            StudentID = c.Int(nullable: false),
                            Grade = c.Int(),
                        })
                    .PrimaryKey(t => t.EnrollmentID)
                    .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
                    .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
                    .Index(t => t.CourseID)
                    .Index(t => t.StudentID);
                
                CreateTable(
                    "dbo.Course",
                    c => new
                        {
                            CourseID = c.Int(nullable: false),
                            Title = c.String(),
                            Credits = c.Int(nullable: false),
                        })
                    .PrimaryKey(t => t.CourseID);
                
            }
            
            public override void Down()
            {
                DropIndex("dbo.Enrollment", new[] { "StudentID" });
                DropIndex("dbo.Enrollment", new[] { "CourseID" });
                DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
                DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
                DropTable("dbo.Course");
                DropTable("dbo.Enrollment");
                DropTable("dbo.Student");
            }
        }
    }
    

    命令 update-database 运行 Up 方法以创建数据库,然后运行 Seed 方法来填充数据库。

现已为数据模型创建SQL Server数据库。 数据库的名称为 ContosoUniversity.mdf文件位于项目的 App_Data 文件夹中,因为这是你在连接字符串中指定的。

可以使用服务器资源管理器SQL Server 对象资源管理器 ( SSOX) 在 Visual Studio 中查看数据库。 在本教程中,你将使用 服务器资源管理器。 在 Visual Studio Express 2012 for Web 中,服务器资源管理器称为“数据库资源管理器”。

  1. 在“ 视图 ”菜单中,单击“ 服务器资源管理器”。

  2. 单击“ 添加连接” 图标。

    显示“数据库资源管理器”窗口的屏幕截图。突出显示了“添加连接”图标。

  3. 如果出现“选择数据源”对话框的提示,请单击“Microsoft SQL Server”,然后单击“继续”。

    显示“选择数据源”对话框的屏幕截图。已选择 Microsoft S Q L 服务器数据源。

  4. 在“添加连接”对话框中,输入“服务器名称” (localdb) \v11.0。 在 “选择或输入数据库名称”下,选择“ ContosoUniversity”。

    显示“添加连接”对话框的屏幕截图。突出显示了示例服务器名称和 Contoso University 数据库。

  5. 单击“确定”

  6. 展开 “SchoolContext” ,然后展开“ ”。

    显示“服务器资源管理器”页的屏幕截图。展开了“学校上下文”和“表格”选项卡。

  7. 右键单击 “学生 ”表,然后单击“ 显示表数据 ”以查看创建的列和插入到表中的行。

    学生表

创建学生控制器和视图

下一步是在应用程序中创建一个 ASP.NET MVC 控制器和视图,该控制器和视图可用于这些表之一。

  1. 若要创建Student控制器,请右键单击解决方案资源管理器中的 Controllers 文件夹,选择“添加”,然后单击“控制器”。 在 “添加控制器 ”对话框中,进行以下选择,然后单击“ 添加”:

    • 控制器名称: StudentController

    • 模板: 具有读/写操作和视图的 MVC 控制器,使用实体框架

    • 模型类: 学生 (ContosoUniversity.Models) 。 (如果在下拉列表中看不到此选项,请生成项目并重试。)

    • 数据上下文类: SchoolContext (ContosoUniversity.Models)

    • 视图: Razor (CSHTML) 。 (default.)

      Add_Controller_dialog_box_for_Student_controller

  2. Visual Studio 将打开 Controllers\StudentController.cs 文件。 你会看到已创建实例化数据库上下文对象的类变量:

    private SchoolContext db = new SchoolContext();
    

    操作Index方法通过读取Students数据库上下文实例的 属性,从学生实体集中获取学生列表:

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

    Student\Index.cshtml 视图在表中显示此列表:

    <table>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstMidName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.EnrollmentDate)
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <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>
        </tr>
    }
    
  3. 按 Ctrl+F5 运行项目。

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

    学生索引页

约定

由于使用了 约定或实体框架做出的假设,为使实体框架能够为你创建完整数据库而必须编写的代码量很少。 其中一些已记录:

  • 实体类名的复数形式用作表名。
  • 使用实体属性名作为列名。
  • 名为 IDclassnameID 的实体属性被识别为主键属性。

你已了解约定可以重写 (例如,你指定了表名不应) 复数,你将在本系列后面的 创建更复杂的数据模型 教程中详细了解约定以及如何重写约定。 有关详细信息,请参阅 Code First 约定

总结

现已创建一个简单的应用程序,该应用程序使用实体框架和SQL Server Express来存储和显示数据。 以下教程介绍如何执行基本的 CRUD (创建、读取、更新、删除) 操作。 可以在此页面底部留下反馈。 请告诉我们你对本教程的这一部分的喜爱程度,以及我们可以如何改进它。

可以在 ASP.NET 数据访问内容映射中找到指向其他实体框架资源的链接。