Mvc5+Entity Framework6 之一-----为ASP.NET MVC应用程序创建一个Entity Framework数据模型

时间:2022-11-28 20:20:00

本文记录在Microsoft MSDN中学习Mvc5 entityframework的笔记(http://msdn.microsoft.com/en-us/data/ee712907.aspx#getstarted

示例应用程序是一个简单的大学网站。用户可以查看和更新​​学生,课程和导师信息,

vs2013+sql2008R2 示例下载

一、创建MVC Web应用程序

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

2.在ASP.NET项目对话框中选择了MVC模板。   单击更改身份验证为No AUrhentication3.设置网站菜单、布局及首页等,打开Views\Shared_Layout.cshtml,替换默认模板中My ASP.NET Application为“欢迎来到宇宙大学”,添加菜单

 

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset= " utf-8 " />
 5 <meta name= " viewport " content= " width=device-width, initial-scale=1.0 ">
 6 <title>@ViewBag.Title - 欢迎来到宇宙大学</title>
 7 @Styles.Render( " ~/Content/css ")
 8 @Scripts.Render( " ~/bundles/modernizr ")
 9 </head>
10 <body>
11 <div  class= " navbar navbar-inverse navbar-fixed-top ">
12 <div  class= " container ">
13 <div  class= " navbar-header ">
14 <button type= " button "  class= " navbar-toggle " data-toggle= " collapse " data-target= " .navbar-collapse ">
15 <span  class= " icon-bar "></span>
16 <span  class= " icon-bar "></span>
17 <span  class= " icon-bar "></span>
18 </button>
19 @Html.ActionLink( " 宇宙大学 "" Index "" Home "nullnew { @class =  " navbar-brand " })
20 </div>
21 <div  class= " navbar-collapse collapse ">
22 <ul  class= " nav navbar-nav ">
23 <li>@Html.ActionLink( " 主页 "" Index "" Home ")</li>
24 <li>@Html.ActionLink( " 关于 "" About "" Home ")</li>
25 <li>@Html.ActionLink( " 学生管理 "" Index "" Student ")</li>
26 <li>@Html.ActionLink( " 注册 "" Index "" Course ")</li>
27 <li>@Html.ActionLink( " 辅导员 "" Index "" Instructor ")</li>
28 <li>@Html.ActionLink( " 部门 "" Index "" Department ")</li>
29 </ul>
30 </div>
31 </div>
32 </div>
33 <div  class= " container body-content ">
34 @RenderBody()
35 <hr />
36 <footer>
37 <p>&copy; @DateTime.Now.Year - 宇宙大学</p>
38 </footer>
39 </div>
40 @Scripts.Render( " ~/bundles/jquery ")
41 @Scripts.Render( " ~/bundles/bootstrap ")
42 @RenderSection( " scripts ", required:  false)
43 </body>
44 </html>

 

3.修改index

4.安装Entityframework6

5.创建数据模型 接下来,将创建Contoso University 应用程序的三个实体类。

 Mvc5+Entity Framework6 之一-----为ASP.NET MVC应用程序创建一个Entity Framework数据模型
在学生Student 和注册Enrollment 实体中存在一对多的关系 ,在课程Course 和Enrollment实体中同样也存在一对多的关系。换句话说,一个学生可以和注册任何数量的课程,一门课程可以有任意数量的学生注册

在Models文件夹中创建一个名为Student.cs一个类文件

 

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }

public virtual ICollection<Enrollment> Enrollments { get; set; }
}
}

 

ID属性将成为此类对应的数据库表的主键列。默认情况下,Entity Framework会把一个名为的ID或classnameID理解为主键属性。 Enrollments 属性是一个导航属性。导航属性指向其他与其相关的实体。在本例中Student 实体中的Enrollments 属性将指向所有的与该Student相关的Enrollment。换句话说,如果一个Student实体在给定数据库中行有两个相关的Enrollment记录(Student的主键ID对应为Enrollment的外键StudentID),Student实体的Enrollments导航属性将包含这两个Enrollment实体。

导航属性通常定义为抽象的,使他们能够使用某些Entity Framework 的功能,如强大的延迟加载功能。(延迟加载将在后面章节读取关联数据解释说明。)

如果某个导航属性可以包含多个实体(如多对多或一对多关系),它的类型必须是可以添加,删除和更新实体的列表类型比如ICollection。

 

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

 

amespace 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; }
}
}

该EnrollmentID属性将是主键,这个实体使用classnameID的模式。在之后的教程中,您将了解为什么我们在Student 实体中使用ID,而在其他实体中使用classnameID,就目前而言,以上说明了两种命名模式您都可以使用。

Grade性是一个枚举。问号后Grade类型声明指示Grade属性是可以为 null。Grade为null与Grade为零不同 – null表示Grade未定义或赋值。

该StudentID属性是一个外键,相应的导航属性是Student。一个Enrollment实体关联一个Student实体,因此,该属性只能关联一个Student实体(和你之前看到的Student.Enrollments导航属性不同,它可以关联多个Enrollment 单位)。

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

如果一个属性被命名为< 导航属性名称 >< 主键属性名称 > (例如,StudentID为Student导航属性因为Student实体的主键是ID),Entity Framework把它解读为外键属性。外键属性还可以只简单命名为< 主键属性名称 > (例如, CourseID因为 Course实体的主键是CourseID).

在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实体相关。

 

创建Database Context

负责协调数据模型和Entity Framework功能的主类是database context 类。你可以从System.Data.Entity.DbContext类派生来创建此类。在您的代码中指定的数据模型中包含哪些实体。您还可以自定义某些实体框架行为。在此项目中 database context类被命名为SchoolContext.

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

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

namespace ContosoUniversity.DAL
{
public class SchoolContext : DbContext
{

public SchoolContext() : base("SchoolContext")
{
}

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>();
}
}
}

指定实体集

连接字符串(之后你将会添加到 Web.config 文件) 的名称会传递给构造函数。

public SchoolContext() : base("SchoolContext")
{
}

还可以通过在连接字符串而不是一个存储在 Web.config 文件中的名称。有关用于指定数据库使用的选项的详细信息,请参见 Entity Framework – Connections and Models.

如果您不显式指定的连接字符串或名称,Entity Framework假定连接字符串的名称与类名称相同。在此示例中的默认连接字符串名称是SchoolContext,和你显式指定的效果相同。

 

指定表名为单数形式

在 OnModelCreating 方法中的 modelBuilder.Conventions.Remove 语句可以阻止表名以复数的形式出现。如果你不这样做,在数据库中所生成的表将命名为StudentsCoursesEnrollments相反,表名称将是StudentCourseEnrollment。开发者对于表名称是否应该为复数有着不同的观点本教程使用的单数形式,但重要的一点是你可以选择任何你更喜欢的形式来包括或省略这行代码。

 

为EF设置初始化数据库的测试数据

您在应用程序运行时,Entity Framework 可以自动创建 (或删除并重新创建) 为数据库。您可以指定当每次运行应用程序执行这些操作还是模型是与现有的数据库不同步时才执行。您也可以编写一个Seed方法,Entity Framework将在创建数据库后自动调用它来填充测试数据。

只有当数据库不存在时,EF默认会创建一个数据库(数据库已经存在并且模型已更改,将会抛出异常)。在本节中,您将指定当模型发生变化时该数据库应被删除并重新创建。删除数据库将导致所有数据的丢失。这开发期间是很OK的,因为在数据库重新创建后Seed的方法将运行,并且将重新创建您的测试数据。但如果正式使用中,每次当您需要更改数据库架构你一般不想失去你的所有数据。稍后你会看到如何通过使用Code First代码迁移更改数据库架构而不是删除和重新创建数据库。

在DAL 文件夹中创建一个名为SchoolInitializer.cs的新类文件并替换的模板代码下面的代码,用来处理当数据库需要创建时将测试数据加载到新的数据库。

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

namespace ContosoUniversity.DAL
{
public class SchoolInitializer : System.Data.Entity. 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{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.Add(s));
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(s
=> context.Enrollments.Add(s));
context.SaveChanges();
}
}
}

Seed方法采用database context对象作为输入参数,并在该方法中向数据库中添加新的实体的对象。对于每个实体类型,代码中创建了新的集合实体,并将它们添加到相应的DbSet属性中,然后将更改保存到数据库。在每一组的实体后调用SaveChanges方法不是必须的,但是当代码写到数据库时发生异常这样做可以帮助你找到问题的根源。

告诉Entity Framework来使用您的类初始值设定项,向entityFramework元素添加在应用程序的Web.config文件 (根项目文件夹中)添加一段代码,如下面的示例所示:

<entityFramework>
<contexts>
<context type="ContosoUniversity.DAL.SchoolContext, ContosoUniversity">
<databaseInitializer type="ContosoUniversity.DAL.SchoolInitializer, ContosoUniversity" />
</context>
</contexts>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>

ontext type指定context class名称和命名空间,databaseinitializer type指定的初始化项类的名称和命名空间 (当你不想让 EF 使用初始化功能时,您可以在context元素中设置一个属性:disableDatabaseInitialization=”true”.)有关详细信息,请参见Entity Framework – Config File Settings.除了在Web.config中设置初始化类,你也可以在代码中通过将Database.SetInitializer语句添加到在中的Global.asax.cs文件的Application_Start方法。更多的信息,请参阅 Understanding Database Initializers in Entity Framework Code First.

基于当前应用程序现在的设置,当您第一次运行应用程序访问数据库时,Entity Framework将对比数据库和你的数据模型 ( SchoolContext和实体类) 。如果两者有差异,应用程序将删除并重新创建数据库。

注:当您应用程序部署到成品 web 服务器时,您必须移除或禁用删除并重新创建该数据库的代码。你会在以后的教程在这一系列中做到。

为EF设置使用SQL Server Express LocalDB数据库

LocalDB是轻量级的版本的 SQL Server 表示数据库引擎。它易于安装和配置,基于用户需求来设计与运行。LocalDB的是SQL Server Express中的一个特殊的执行模式,使您可以使用.mdf文件来运行数据库。如果您希望把数据库集成在项目中,你可以把 LocalDB 数据库文件放在的 web 项目的App_Data文件夹下。在 SQL Server Express用户实例功能也能让您能够使用的.mdf文件,但用户实例功能不推荐使用 ;因此,推荐LocalDB 使用的.mdf文件。在 Visual Studio 2012 和以后的版本中,默认情况下,Visual Studio将 安装 LocalDB。

通常 SQL Server Express 不适合用于部署后正式使用的应用程序。LocalDB 更是不建议与部署后正式的应用程序使用因为它并不是针对 IIS设计使用的。

在本教程中,您将使用 LocalDB。打开应用程序的Web.config文件并在appSettings元素之前添加connectionStrings元素,如下面的示例中所示。(请确保您更新Web.config文件中的根项目文件夹。您不需要更新在文件夹Views 中的 Web.config文件.)

<connectionStrings>
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity1;Integrated Security=SSPI; providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

添加的连接字符串指定Entity Framework将使用一个名为ContosoUniversity1.mdf的 LocalDB 数据库。(数据库尚不存在 ,EF 将会创建它。)如果你想要在您的App_Data文件夹中创建数据库,则可以添加AttachDBFilename=|DataDirectory|\ContosoUniversity1.mdf到连接字符串。有关连接字符串的详细信息,请参见SQL Server Connection Strings for ASP.NET Web Applications.

实际上你不一定要在Web.config文件中设置连接字符串。如果您不提供连接字符串,Entity Framework将根据您的context class使用一个默认值基于。有关详细信息,请参见Code First to a New Database。

创建Student控制器和视图

您现在可以创建网页来显示数据,请求数据的过程将自动触发创建数据库。首先你将要创建一个新的控制器。在此之前,生成项目,使实体类和context classes对 MVC 控制器可用。

  1. 在解决方案资源管理器中鼠标右键单击控制器文件夹中的,选择添加,然后再单击控制器.
  2. 在添加脚手架对话框中,选择包含视图MVC 5 控制器(使用Entity Framework),使用实体框架.
  3. 在添加控制器对话框中,选择以下选项,然后单击添加:
    • 控制器的名称: StudentController.
    • 模型类:Student (ContosoUniversity.Models)。(如果看不到此选项下拉列表中的,生成项目并再试一次)。
    • 数据上下文类(Data context class): SchoolContext (ContosoUniversity.Models).
    • 其他选项使用默认值。Mvc5+Entity Framework6 之一-----为ASP.NET MVC应用程序创建一个Entity Framework数据模型
      1. 当您单击添加时,控制器创建一个 StudentController.cs 文件和一组视图 (.cshtml 文件) 与控制器的工作。当您使用Entity Framework创建项目时,你还可以使用一些控制器的附加功能: 只创建您的第一个模型类,不要创建连接字符串,然后在添加控制器框中指定新的上下文类。控制器同样将会创建您的 DbContext类和您的连接字符串,控制器和视图。

  4. Visual Studio 将打开Controllers\StudentController.cs文件。您将看到一个类已创建并且数据库上下文(database context )对象实例化:
    private SchoolContext db = new SchoolContext();

    Index方法获取database context对象的Students属性中的Students实体集合:

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

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

    <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.ID }) |
    @Html.ActionLink(
    "Details", "Details", new { id=item.ID }) |
    @Html.ActionLink(
    "Delete", "Delete", new { id=item.ID })
    </td>
    </tr>
    }

    OK,运行吧

  5. 约定

    Entity Framework的这些约定能够为您创建一个完整的数据库而是您必须写的这些代码编写量最少化。以下是一些已经说明过或你不知道的:

    • 实体类名的复数形式作为表名
    • 实体属性名称被用于列名
    • 实体属性中命名为ID或classnameID会被认为是主键
    • 如果一个属性命名为< 导航属性名称 >< 主键属性名称 > (例如,StudentID为Student导航属性因为Student实体的主键是ID)。外键属性还可以仅仅命名为 < 主键属性名称 > (例如 EnrollmentID,因为 Enrollment实体的主键是EnrollmentID).