为 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。 有关这些工作流之间的差异以及有关如何为方案选择最佳工作流的指导的信息,请参阅 Entity Framework 开发工作流

MVC

示例应用程序基于 ASP.NET MVC 构建。 如果想要使用 ASP.NET Web 窗体模型,请参阅 “模型绑定”和“Web 窗体 ”教程系列以及 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 部署需要 VS 2013 版本的 Windows Azure SDK
.NET 4.5 显示的大多数功能将在 .NET 4 中运行,但有些功能不起作用。 例如,EF 中的枚举支持需要 .NET 4.5。
实体框架 5
Windows Azure SDK 2.1 如果跳过 Windows Azure 部署步骤,则不需要 SDK。 发布 SDK 的新版本后,链接将安装较新版本。 在这种情况下,可能需要根据新的 UI 和功能调整一些说明。

问题

如果存在与本教程不直接相关的问题,可以将它们 发布到 ASP.NET Entity Framework 论坛实体框架和 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 月起安装最新更新和适用于 .NET 的 Azure SDK。 可以使用以下链接获取所有这些内容:

用于 .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>

此代码会更改以下内容:

  • 将“My ASP.NET MVC Application”和“your logo here”的模板实例替换为“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 运行站点。 你会看到主菜单的主页。

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

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

导航属性通常被定义为 virtual 可以利用某些 Entity Framework 功能,例如 延迟加载。 (延迟加载将在稍后解释,在 阅读本系列后面的相关数据 教程。

如果导航属性可以具有多个实体 (如多对多或一对多关系),那么导航属性的类型必须是可以添加、 删除和更新条目的容器,如 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)。None]下一教程中的属性。 简单来说,此特性让你能自行指定主键,而不是让数据库自动指定主键。

创建数据库上下文

协调给定数据模型的 Entity Framework 功能的主类是 数据库上下文 类。 通过派生自 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.Remove OnModelCreating 方法中的语句可防止表名被复数化。 如果未执行此操作,则生成的表将命名 StudentsCourses并且 Enrollments。 相反,表名将是 StudentCourse并且 Enrollment。 开发者对表名称是否应为复数意见不一。 本教程使用单一窗体,但要点是,可以通过包括或省略此代码行来选择你喜欢的表单。

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 和早期版本中,SQL Server Express(不含 LocalDB)默认随 Visual Studio 一起安装;如果使用 Visual Studio 2010,则必须手动安装它。

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

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

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

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

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

设置和执行代码优先迁移

首次开始开发应用程序时,数据模型会频繁更改,每次模型更改时,它都会与数据库不同步。 可以将 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 方法

种子方法在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 该方法。 它会检查实体是否已存在,如果实体不存在,则插入该实体。 此方法将保留你在迁移运行时对注册等级所做的更改。 该代码循环访问列表的每个成员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)DB

  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

    • 模板: 使用 Entity Framework 使用读/写操作和视图的 MVC 控制器。

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

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

    • 视图: Razor (CSHTML)。 (默认值)。

      Add_Controller_dialog_box_for_Student_controller

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

    private SchoolContext db = new SchoolContext();
    

    操作Index方法通过读取Students数据库上下文实例的属性从 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

    学生索引页

约定

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

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

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

总结

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

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