您的位置:首页 > 编程语言 > ASP

使用 Visual Studio 的 ASP.NET Core MVC 和 Entity Framework Core 入门 (1 of 10)

2017-07-18 11:28 1241 查看
原文网址:https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/intro

By Tom Dykstra and Rick Anderson

Contoso University 示例 Web 应用程序示范了如何使用 Entity Framework Core 1.1 和 Visual Studio 2017 创建 ASP.NET Core 1.1 MVC Web应用程序。

这个示例应用程序是一个虚构的 Contoso University 网站。它包含了诸如学生录取、课程创建、和教师作业等功能。这是系列教程中的第一个,介绍了如何从头开始构建 Contoso University 大学示例应用程序。

Download or view the completed application(下载或查看完整的应用程序).

EF Core 1.1 是 EF 的最新版本,但还没有具备 EF 6.x 的所有功能。有关如何在 EF 6.x 和 EF Core 1.0 之间进行选择的更多信息,请查看 EF Core vs. EF6.x。 如果你选择 EF 6.x ,请查看 the previous version of this tutorial series.


[!注意]
Visual Studio 2015 版本对应的教程, 请查看 VS 2015 version of ASP.NET Core documentation in PDF format.


预备知识

要事先安装好 Visual Studio 2017 的工作负载 ASP.NET and web development ([b]ASP.NET 和 web 开发)[/b] 和 .NET Core cross-platform development workloads ([b].NET Core 跨平台开发)[/b] .

疑难解答

如果遇到你无法解决的问题,通常可以将你自己的代码和 已完成项目 进行比较来找到解决方案。有关常见错误及其解决方法的列表,请查看 the Troubleshooting section of the last tutorial in the series。如果无法找到你需要的内容,你可以把遇到的问题发到 StackOverflow.com 的 ASP.NET Core 或者 EF Core 上面。


[!提示]
这是一系列的 10个教程。每一个都是建立在前一个教程的基础之上。在每次成功地完成教程后,务必为项目保存一个副本,当遇到问题,你可以从前一个教程重新来过而不是回到整个系列的开头。


Contoso University web 应用程序

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

用户可以查看及更新学生、课程、和教师信息。下面是你将要创建的几个页面:









这个网站的 UI 样式跟内置模板生成的内容关系密切,所以本教程能主要关注如何使用 Entity Framework。

创建一个 ASP.NET Core MVC web 应用程序

打开 Visual Studio 然后创建一个新的 ASP.NET Core C# Web 项目,并命名为 "ContosoUniversity"。

File(文件) 菜单, select New(新建) > Project(项目)

从左边的窗格,选择 Templates(模板) > Visual C# > Web

选择 ASP.NET Core Web 应用程序 (.NET Core) 项目模板

输入名称 ContosoUniversity 然后单击 OK(确定)





等待 新建 ASP.NET Core Web 应用程序 (.NET Core) 对话框出现

选择 ASP.NET Core 1.1Web 应用程序 模板

注意: 本教程需要 ASP.NET Core 1.1 和 EF Core 1.1 或是更高的版本 -- 确保 没有选中 ASP.NET Core 1.0

确保 Authentication(身份验证) 选择的是 No Authentication(不进行身份验证).

单击 OK(确定)





设置网站样式

对网站的菜单、布局和主页进行一点简单的更改。

打开 Views/Shared/_Layout.cshtml 然后进行以下的更改:

把每一处的 "ContosoUniversity" 都改为 "Contoso University",一共有三处。

添加菜单项 Students(学生), Courses(课程), Instructors(教师), 和 Departments(部门), 并删除 Contact(联系人) 菜单项

修改后的代码如以下所示.

1     <div class="navbar-collapse collapse">
2         <ul class="nav navbar-nav">
3             <li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
4             <li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
5             <li><a asp-area="" asp-controller="Students" asp-action="Index">Students</a></li>
6             <li><a asp-area="" asp-controller="Courses" asp-action="Index">Courses</a></li>
7             <li><a asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a></li>
8             <li><a asp-area="" asp-controller="Departments" asp-action="Index">Departments</a></li>
9        </ul>
10     </div>


[!code-htmllayout]

在 Views/Home/Index.cshtml 文件中,用下面的代码替换这个文件的内容,将有关 ASP.NET 和 MVC 的文本替换为关于这个应用程序的文本。

1 @{
2     ViewData["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 Core in an
14             ASP.NET Core MVC 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 a series of tutorials.</p>
20         <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</a></p>
21     </div>
22     <div class="col-md-4">
23         <h2>Download it</h2>
24         <p>You can download the completed project from GitHub.</p>
25         <p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnet/data/ef-mvc/intro/samples/cu-final">See project source code »</a></p>
26     </div>
27 </div>


[!code-htmlIndex.cshtml]

按 CTRL+F5 运行项目或是从菜单中选择 Debug(调试) > Start Without Debugging(开始执行(不调试)),你就可以在主页标签中看到根据这些教程创建的页面。





Entity Framework Core NuGet 包

要添加 EF Core 的支持到当前项目中, 需要安装目标数据库的支持程序。在本教程中,需要安装 SQL Server 支持程序Microsoft.EntityFrameworkCore.SqlServer

要安装这个软件包,需要在 程序包管理器控制台 (PMC) 中输入以下的命令.(从 Tools(工具) 菜单,选择 NuGet Package Manager(NuGet包管理器) > Package Manager Console(程序包管理器控制台))。

Install-Package Microsoft.EntityFrameworkCore.SqlServer

这个包及其依赖项为 EF 提供了 run-time(运行时) 支持。稍后将添加一个工具包,在Migrations(迁移) 教程中。

有关可用于 Entity Framework Core 的其他数据库支持程序的信息,请查看 Database providers.

创建数据模型

下一步是要为 Contoso University 应用程序创建模型在。 你可以从下面三个实体类开始。





Student
Enrollment
两个实体之间是 一对多 的关系。
Course
Enrollment
是两个实体之间也是一对多的关系。换句话说, 一个学生可以加入到任意数量的课程中,一门课程能允许很多学生加入进去。

在下面的部分,你将为这些实体中的每一个创建一个类。

学生实体





在项目文件夹中,创建一个名为 Models 的文件夹。

在 Models 文件夹中,创建一个名为 Student.cs 的类,并且用以下的代码来替换掉由模板生成的代码。

[!code-csharpMain]

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
13         public ICollection<Enrollment> Enrollments { get; set; }
14     }
15 }


ID
属性将成为与这个类相对应的数据库表的主键列。在默认情况下, Entity Framework 会把名为
ID
classnameID
的属性解释为主键。

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

如果一个导航属性可能容纳多个实体(象在 多对多 或 一对多 关系中),它的类型必须是可以在其中进行添加、删除、和更新条目的列表,例如
ICollection<T>
。你可以指定
ICollection<T>
或类似于
List<T>
HashSet<T>
的类型。如果你指定
ICollection<T>
,EF在默认情况下将创建一个
HashSet<T>
集合。

Enrollment 实体





在 Models 文件夹,创建 Enrollment.cs 然后将里面的代码替换为以下的代码:

[!code-csharpMain]

1 namespace ContosoUniversity.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 Course Course { get; set; }
16         public Student Student { get; set; }
17     }
18 }


EnrollmentID
属性将被作为主键;这里使用
classnameID
的模式而不是
ID
是因为要在
Student
实体类中也看到这个属性。通常你会选择一种模式并在整个数据模型中使用它。在这里, 这个变化表明你可以使用两个之中的任意一种模式。在 later tutorial(最后的教程)中,你将看到在没有类的情况下使用 ID 使得在数据模型中实现继承变得更加容易。

Grade
属性是一个
enum(枚举)
Grade
类型声明后面的问号表示
Grade
属性可以为空值。一个 grade 为 null 不等同于 0 -- null 表示 grade 的值为未知或是还没有被分配。

StudentID
属性是一个外键,与之相对应的导航属性是
Student
。 一个
Enrollment
实体与一个
Student
实体关联,所在这个属性能容纳单个
Student
实体(不象前面的
Student.Enrollments
导航属性,它可以容纳多个
Enrollment
实体)。

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

Entity Framework 将命名为
<navigation property name><primary key property name>(导航属性名+主键属性名)
(例如, 因为
Student
实体的主键是
ID
,所以对应的导航属性就是
StudentID
)的属性解释为外键。外键属性也可以被简单地命名 为
<primary key property name>
(例如,
CourseID
是因为
Course
实体的主键是
CourseID
)。

Course 实体





在 Models 文件夹, 创建 Course.cs 类并用以下代码替换原来的代码:

[!code-csharpMain]

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
13         public ICollection<Enrollment> Enrollments { get; set; }
14     }
15 }


Enrollments
属性是一个导航属性。一个
Course
实体能关联任意数量的
Enrollment
实体。

我们将在本系列教程的 later tutorial 中详细介绍
DatabaseGenerated
特性。基本上,这个特性允许你输入主键而不是由数据库来生成它。

t创建数据库上下文

为给定的数据模型协调 Entity Framework 功能的主类是数据库上下文。通过
Microsoft.EntityFrameworkCore.DbContext
类派生来创建这个类。在代码中说明哪些实体被包含在数据模型中。你还可以自定义某些 Entity Framework 的行为。在本项目中,这个类被命名为
SchoolContext


在项目文件夹中,创建一个名为 Data 的文件夹。

在 Data 文件夹中创建一个新的类文件,命名为 SchoolContext.cs ,然后用以下代码替换模板生成的代码:

[!code-csharpMain]

1 using ContosoUniversity.Models;
2 using Microsoft.EntityFrameworkCore;
3
4 namespace ContosoUniversity.Data
5 {
6     public class SchoolContext : DbContext
7     {
8         public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
9         {
10         }
11
12         public DbSet<Course> Courses { get; set; }
13         public DbSet<Enrollment> Enrollments { get; set; }
14         public DbSet<Student> Students { get; set; }
15     }
16 }


这段代码为每一个实体集创建了个对应的
DbSet
属性, 在 Entity Framework 术语中, 一个实体集通常对应于一个数据库的表,一个实体则对应于表中的一行。

你可以省略掉
DbSet<Enrollment>
DbSet<Course>
的声明语句因为他们的作用是一样的,Entity Framework 将隐式包含它们,因为
Student
实体引用
Enrollment
实体,而
Enrollment
实体引用了
Course
实体。

创建数据库时,EF 创建的表名与
DbSet
属性名称相同。集合的属性名称通常为(实体名的)复数(Students 而不是 Student)。但是开发都在表名是否应该多元化方面的意见是不统一的。在本教程中,你可以通过指定单数表名在 DbContext 中重写默认的表名。为此,请在最后一个
DbSet
属性之后添加以下突出显示的代码。()

[!code-csharpMain]

1 #elif TableNames
2 #region snippet_TableNames
3 using ContosoUniversity.Models;
4 using Microsoft.EntityFrameworkCore;
5
6 namespace ContosoUniversity.Data
7 {
8     public class SchoolContext : DbContext
9     {
10         public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
11         {
12         }
13
14         public DbSet<Course> Courses { get; set; }
15         public DbSet<Enrollment> Enrollments { get; set; }
16         public DbSet<Student> Students { get; set; }
17
18         //添加以下 OnModelCreating 方法代码
19         protected override void OnModelCreating(ModelBuilder modelBuilder)
20         {
21             modelBuilder.Entity<Course>().ToTable("Course");
22             modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
23             modelBuilder.Entity<Student>().ToTable("Student");
24         }
25     }
26 }
27 #endregion


使用依赖注入注册 context(上下文)

ASP.NET Core 默认实现 dependency injection(依赖注入)。服务 (例如 EF 数据库上下文)在应用程序启动期间通过依赖注入注册。需要这些服务的组件(例如 MVC 控制器)通过构造函数参数提供这些服务。在本教程后面的部分, 你将可以看到获得上下文实例的控制器构造函数的代码。

要将
SchoolContext
注册为服务, 请打开 Startup.cs,并将突出显示的行添加到
ConfigureServices
方法中。

[!code-csharpMain]

1  public Startup(IHostingEnvironment env)
2         {
3             var builder = new ConfigurationBuilder()
4                 .SetBasePath(env.ContentRootPath)
5                 .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
6                 .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
7                 .AddEnvironmentVariables();
8             Configuration = builder.Build();
9         }
10
11         public IConfigurationRoot Configuration { get; }
12
13 // This method gets called by the runtime. Use this method to add services to the container.
14         #region snippet_SchoolContext
15         public void ConfigureServices(IServiceCollection services)
16         {
17             // Add framework services.
18             services.AddDbContext<SchoolContext>(options =>
19                 options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
20
21             services.AddMvc();
22         }
23 #endregion


通过调用
DbContextOptionsBuilder
对象中的方法,将连接字符串的名称传递到上下文中。对于本地开发,ASP.NET Core configuration system 从 appsettings.json 文件中读取连接字符串。

ContosoUniversity.Data
Microsoft.EntityFrameworkCore
命名空间添加
using
引用声明,然后 生成 这个项目。

[!code-csharpMain]

打开 appsettings.json 文件并添加一个如下面示例显示的连接字符串。

[!code-jsonappsettings]

1 {
2   "ConnectionStrings": {
3     "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
4   },
5   "Logging": {
6     "IncludeScopes": false,
7     "LogLevel": {
8       "Default": "Warning"
9     }
10   }
11 }


SQL Server Express LocalDB

这个连接字符串指定了一个 SQL Server LocalDB 数据库。 LocalDB 是一个轻量级版本的 SQL Server Express(速成版) 数据库引擎,用于应用程序开发,不用于生产用途。LocalDB 按需要启动并以用户模式运行, 所以没有什么复杂的配置。默念情况下,LocalDB 在
C:/Users/<user>
文件夹中创建 .mdf 数据库文件。

使用测试数据添加代码实现初始化数据库

Entity Framework 将会为你创建一个空的数据库, 在本节中, 你将编写一个在创建数据库调用的方法,以便用测试数据填充它。

在这里,你将使用
EnsureCreated
方法自动创建数据库。在 later tutorial 你将看到如何通过使用代码优先迁移来更改数据库架构而不是删除和重新创建数据库来处理模型的更改。

在 Data 文件夹中,创建一个命名为 DbInitializer.cs 的类文件,并且用以下的代码替换掉模板生成的代码,使得数据库在需要的时候通被创建并将测试数据加载到这个新数据库中。

[!code-csharpMain]

1 #region snippet_Intro
2 using ContosoUniversity.Models;
3 using System;
4 using System.Linq;
5
6 namespace ContosoUniversity.Data
7 {
8     public static class DbInitializer
9     {
10         public static void Initialize(SchoolContext context)
11         {
12             context.Database.EnsureCreated();
13
14             // Look for any students.
15             if (context.Students.Any())
16             {
17                 return;   // DB has been seeded
18             }
19
20             var students = new Student[]
21             {
22             new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
23             new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
24             new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
25             new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
26             new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
27             new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
28             new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
29             new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
30             };
31             foreach (Student s in students)
32             {
33                 context.Students.Add(s);
34             }
35             context.SaveChanges();
36
37             var courses = new Course[]
38             {
39             new Course{CourseID=1050,Title="Chemistry",Credits=3},
40             new Course{CourseID=4022,Title="Microeconomics",Credits=3},
41             new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
42             new Course{CourseID=1045,Title="Calculus",Credits=4},
43             new Course{CourseID=3141,Title="Trigonometry",Credits=4},
44             new Course{CourseID=2021,Title="Composition",Credits=3},
45             new Course{CourseID=2042,Title="Literature",Credits=4}
46             };
47             foreach (Course c in courses)
48             {
49                 context.Courses.Add(c);
50             }
51             context.SaveChanges();
52
53             var enrollments = new Enrollment[]
54             {
55             new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
56             new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
57             new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
58             new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
59             new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
60             new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
61             new Enrollment{StudentID=3,CourseID=1050},
62             new Enrollment{StudentID=4,CourseID=1050},
63             new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
64             new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
65             new Enrollment{StudentID=6,CourseID=1045},
66             new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
67             };
68             foreach (Enrollment e in enrollments)
69             {
70                 context.Enrollments.Add(e);
71             }
72             context.SaveChanges();
73         }
74     }
75 }
76 #endregion


代码检查数据库中是否有学生,如果没有,它假定数据库是新的并且需要使用测试数据进行种子化(将测试植入到数据库中)。它将测试数据加载到数据组都不是
List<T>
集合中以优化性能。

I在 Startup.cs 文件中,修改
Configure
方法以便在应用程序启动时调用这个种子方法。首先,添加上下文到方法签名中以便 ASP.NET 依赖注入能将其提供给你的
DbInitializer(数据库初始化)
类。

[!code-csharpMain]

然后在 Configure 方法的末尾调用你的 DbInitializer.Initialize 方法。

[!code-csharpMain]

1         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
2         #region snippet_ConfigureSignature
3         public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, SchoolContext context)
4         {
5             loggerFactory.AddConsole(Configuration.GetSection("Logging"));
6             loggerFactory.AddDebug();
7             #endregion
8
9             if (env.IsDevelopment())
10             {
11                 app.UseDeveloperExceptionPage();
12                 app.UseBrowserLink();
13             }
14             else
15             {
16                 app.UseExceptionHandler("/Home/Error");
17             }
18
19             app.UseStaticFiles();
20
21             #region snippet_RouteAndSeed
22             app.UseMvc(routes =>
23             {
24                 routes.MapRoute(
25                     name: "default",
26                     template: "{controller=Home}/{action=Index}/{id?}");
27             });
28
29             DbInitializer.Initialize(context);
30             #endregion
31         }


现在第一次运行这个应用程序的时候数据库将会被创建并且植入测试数据。 每当更改了数据模型,你可以删除这个数据库,更改种子方法,然后同样的方式重新开始新的数据库。在后面的教程中你可以看到当数据模型更改时如何修改数据库,而不删除了再重建它。

创建控制器和视图

接下来,你将在 Visual Studio 中使用基架引擎添加一个 MVC 控制器和视图,以便使用 EF 查询和保存数据。

CRUD 操作方法和视图的自动创建称为
基架。基架不同于代码生成,因为搭建代码是一个起点,你可以根据自己的需要进行修改,而通常情况下你不修改生成的代码。当需要自定义生成的代码时,你可以使用
partial classes (分部类)或者当对象发生变化的时候重新生成代码。

Solution Explorer(项目资源管理器) 中鼠标右键单击 Controllers 文件夹,然后选择 Add(添加) > New Scaffolded Item(新搭建基架的项目).

Add MVC Dependencies(添加 MVC 依赖项) 对话框中,选择 Minimal Dependencies(最小依赖项), 然后选择 Add(添加).





Visual Studio 添加基架控制器所需要的依赖项,包括有设计时(design-time) EF 功能的包(
Microsoft.EntityFrameworkCore.Design
);还包括一个从现有的数据库搭建 DbContext(数据库上下文) 的包 (
Microsoft.EntityFrameworkCore.SqlServer.Design
);创建了一个 ScaffoldingReadMe.txt 文件,当然你可以删除它。

再次在 Solution Explorer(项目资源管理器) 中鼠标右键单击 Controllers 文件夹,然后选择 Add(添加) > New Scaffolded Item(新搭建基架的项目)

Add Scaffold(添加基架) 对话框中:

选择 MVC controller with views, using Entity Framework(视图使用 Entity Framework 的 MVC 控制器)

单击 Add(添加)

Add Controller(添加控制器) 对话框中:

Model class 处选择 Student.

Data context class 处选择 SchoolContext

使用默认的控制器名称 StudentsController

Click Add.
单击 Add(添加)





当你单击了 Add(添加), Visual Studio 基架引擎将创建一个 StudentsController.cs 文件和一组跟控制器一起工作的视图(.cshtml 文件)。

(如果你没有根据前面的教程首先手动创建数据库上下文,基架引擎同样可以帮你创建。你可以在 Add Controller(添加控制器) 对话框中单击 Data context class 右边的 "+" (加号按钮)指定一个新的上下文类。然后 Visual Studio 就会创建你的
DbContext
类以及控制器和视图。)

你会注意到控制器以
SchoolContext
作为构造函数参数。

[!code-csharpMain]

ASP.NET 依赖注入将负责把
SchoolContext
实例传递到控制器中。前面的时候已经在 Startup.cs 文件进行过配置了。

控制器包含一个
Index
操作方法, 它显示数据库中的所有学生。 这个方法通过读取数据库上下文实例中的
Students
属性从 Students 实体集中获取学生的列表:

[!code-csharpMain]

在本教程后面的部分中,你将了解此代码中的异步编程元素。

Views/Students/Index.cshtml 视图在表格中显示这些列表:

[!code-html]

按 CTRL+F5 运行项目或从菜单中选择 Debug(调试) > Start Without Debugging(开始执行(不调试))

单击 Students 标签查看从
DbInitializer.Initialize
方法插入的测试数据。 取决于浏览器窗口的大小,你将在页面的顶部看到
Student
链接或是需要单击右上角的导航图标才能看到。









查看数据库

当你启动应用程序时,
DbInitializer.Initialize
方法将调用
EnsureCreated
。EF 看到没有数据库时会创建一个,然后
Initialize
方法代码的其余部分将数据填充了数据库。 你可以在 Visual Studio 中使用 SQL Server Object Explorer(SQL Server 对象资源管理器) (SSOX) 查看数据库。

关闭浏览器。

如果 SSOX 窗口还没有打开,可以在 Visual Studio 的 View(视图) 菜单中打开它。

在 SSOX中,单击 (localdb)\MSSQLLocalDB > Databases(数据库),然后单 appsettings.json 文件中连接字符串对应的数据库名称条目。

在你的数据库中展开 Tables(表) 节点查看表。





鼠标右键单击 Student 表并单击 View Data(查看数据)





.mdf 和 .ldf 数据库文件在 C:Users 文件夹中。

因为在应用程序启动时运行的初始化方法中调用
EnsureCreated
,你现在可以改变一下
Student class
, 删除数据库, 再次运行程序,这时候数据库会自动重建以匹配你做的修改。如果你添加一个
EmailAddress
属性到
Student
类,你可以看到一个新的
EmailAddress
列在重新创建的表中。

约定

因为使用了约定 或 Entity Framework 所做的假设,可以使 Entity Framework 能用必须编写的最小代码来创建一个完整的数据库。

DbSet
属性的名字作为表名。对
DbSet
属性未被引用的实体,实体类名作为表名。

Entity 属性名用为列名

被命名为 named ID 或 classnameID 的 Entity 属性被识别为主键属性。

如果一个属性被命令为 (例如,
Student
的导航属性
StudentID
, 因为
Student
实体的主键是
ID
) 会被解释为一个外键属性。外键属性也可以简单地命名为 (例如,
EnrollmentID
是因为
Enrollment
实体的主键是
EnrollmentID


当然我们也可以重写常规行为。例如,你可以显示地指定表名,就象在本教程前面部分看到的一样。并且你可以设置列名,并将任何属性设为主键或外键,相关的内容你在本教程中查看 later tutorial(复杂数据模型)

异步代码

异步编程是 ASP.NET Core 和 EF Core 的默认模式

一个 Web
服务器的可用线程数量是有限的,在高负载的情况下,所有的可用线程可能都正在使用中。当这种情况发生时,服务器不能处理新的请求直到有线程被释放。使用同步代码,当进程处理等待
I/O
完成时,它的线程会被释放,以供服务器用于处理其他的请求。因此,异步代码能更有效地使用服务器的资源,并使服务器能够处理更多的通信,而不会出现延迟。

异步代码在运行时会引入少量的开销,但对于低流量的情况下,性能的损失是微不足道的。而对于高流量的情况,潜在的性能改进是很大的。

在以下的代码中,
async
关键字、
Task<T>
返回值、
await
关键字、还有
ToListAsync
方法使得代码能够以异步方式执行。

[!code-csharpMain(异步控制器完整代码)]

1         public async Task<IActionResult> Index(string sortOrder)
2         {
3             ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
4             ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
5             var students = from s in _context.Students
6                            select s;
7             switch (sortOrder)
8             {
9                 case "name_desc":
10                     students = students.OrderByDescending(s => s.LastName);
11                     break;
12                 case "Date":
13                     students = students.OrderBy(s => s.EnrollmentDate);
14                     break;
15                 case "date_desc":
16                     students = students.OrderByDescending(s => s.EnrollmentDate);
17                     break;
18                 default:
19                     students = students.OrderBy(s => s.LastName);
20                     break;
21             }
22             return View(await students.AsNoTracking().ToListAsync());
23 }


async
关键字通知编译器为方法的某些部分生成回调,并自动创建返回的
Task<IActionResult>
对象。

返回类型
Task<IActionResult>
正在进行的工作,其结果是一个
IActionResult
类型。

await
会导致编译器将方法拆分为两个部分。第一部分以异步启动的操作结束。第二部分被放入一个回调方法中,当操作完成后被调用。

ToListAsync
ToList
扩展方法的异步版本。

在编写使用 Entity Framework 的异步代码时需要注意的一些事项:

只异步执行那些发送到数据库导致查询或命令的语句。这包括,例如,
ToListAsync
,
SingleOrDefaultAsync
, 和
SaveChangesAsync
。不包括,例如,只会改变
IQueryable
的语句,诸如
var students = context.Students.Where(s => s.LastName == "Davolio")


EF context 是非线程安全的:不要尝试并行执行多个操作。当你调用任何的异步 EF 方法,都必须使用
await
关键字。

如果要利用异步代码的性能优势,确保你正在使用的任何库程序包(比如分布),如果调用任何导致查询发送到数据库的 Entity Framework 方法也使用异步方法。

有关 .NET 中关于异步编程的更多信息,请查看 Async Overview(异步概述).

小结

现在你已经可以创建一个使用 Entity Framework Core 和 SQL Server Express LocalDB 来存储和显示数据的简单应用程序。在接下来的教程中,你将学习如何执行基本的 CRUD(create, read, update, delete) 操作。

下一篇:增、查、改、删 -- EF Core 与 ASP.NET Core MVC 教程(2 of 10)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐