用ASP.NET Core MVC 和 EF Core 构建Web应用 (一)

系统必备

  • .NET Core 2.0.0 SDK 或更高版本。
  • 已安装 ASP.NET 和 Web 开发工作负载的 Visual Studio 2017 15.3 版或更高版本。

创建Web应用程序

打开 Visual Studio 并创建一个新 ASP.NET Core C# web 项目名为”ContosoUniversity”。

  • 文件菜单上,选择新建 > 项目

  • 从左窗格中,选择已安装 > Visual C# > Web

  • 选择“ASP.NET Core Web 应用程序”项目模板。

  • 输入ContosoUniversity作为名称,然后单击确定

  • 在 “新建 ASP.NET Core Web 应用程序” 对话框,选择ASP.NET Core 2.1和 Web 应用程序 (模型-视图-控制器)模板。

  • 注意:本教程需要安装 ASP.NET Core 2.0 和 EF Core 2.0 或更高版本。

  • 请确保身份验证设置为不进行身份验,单击“确定”。

 

修改页面菜单布局和主页

打开Views/Shared/_Layout.cshtml并进行以下更改:

  • 将文件中的”ContosoUniversity”更改为”Contoso University”。 需要更改三个地方。

  • 添加菜单项StudentsCoursesInstructors,和Department,并删除Contact菜单项。

1 <li><a asp-area="" asp-controller="Students" asp-action="Index">Students</a></li>
2 <li><a asp-area="" asp-controller="Courses" asp-action="Index">Courses</a></li>
3 <li><a asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a></li>
4 <li><a asp-area="" asp-controller="Departments" asp-action="Index">Departments</a></li>

Views/Home/Index.cshtml,将文件的内容替换为以下代码以将有关 ASP.NET 和 MVC 的内容替换为有关此应用程序的内容:

@{
    ViewData["Title"] = "Home Page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core MVC web application.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial &raquo;</a></p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code &raquo;</a></p>
    </div>
</div>
View Code

CTRL + F5 来运行该项目或从菜单选择调试 > 开始执行不调试。 你会看到首页和将通过这个教程创建的页对应的选项卡。

数据访问部分使用 EF Core

若要为项目添加 EF Core 支持,需要安装相应的数据库驱动包。 本教程使用 SQL Server,相关驱动包Microsoft.EntityFrameworkCore.SqlServer。 该包包含在Microsoft.AspNetCore.All 包中,因此不需要手动安装。此包和其依赖项 (Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.Relational) 一起提供 EF 的运行时支持。 

创建数据模型

创建 Contoso 大学应用程序的实体类,从以下三个实体类开始。

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

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

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

Enrollments属性是导航属性。 导航属性中包含与此实体相关的其他实体。 在这个案例下,Student entity中的Enrollments属性会保留所有与Student实体相关的Enrollment。 换而言之,如果在数据库中有两行描述同一个学生的修读情况 (两行的 StudentID 值相同,而且 StudentID 作为外键和某位学生的主键值相同),Student实体的Enrollments导航属性将包含那两个Enrollment实体。

如果导航属性可以具有多个实体 (如多对多或一对多关系),那么导航属性的类型必须是可以添加、 删除和更新条目的容器,如ICollection<T>。 你可以指定ICollection<T>或实现该接口类型,如List<T>HashSet<T>。 如果指定ICollection<T>,EF在默认情况下创建HashSet<T>集合。

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

 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模式而不是如Student实体那样直接使用ID。 通常情况下,你选择一个主键模式,并在你的数据模型自始至终使用这种模式。 在这里,使用了两种不同的模式只是为了说明你可以使用任一模式来指定主键。 

Grade属性是enum。 Grade声明类型后的?表示Grade属性可以为 null。 评级为 null 和评级为零是有区别的 –null 意味着评级未知或者尚未分配。

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

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

如果一个属性名为<导航属性名><主键属性名>,Entity Framework 就会将这个属性解析为外键属性(例如,Student实体的主键是IDStudentEnrollment的导航属性所以Enrollment实体中StudentID会被解析为外键)。 此外还可以将需要解析为外键的属性命名为<主键属性名>(例如,CourseID由于Course实体的主键所以CourseID也被解析为外键)。

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

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

创建数据库上下文

使得给定的数据模型与 Entity Framework 功能相协调的主类是数据库上下文类。 可以通过继承 Microsoft.EntityFrameworkCore.DbContext 类的方式创建此类。 在该类中你可以指定数据模型中包含哪些实体。 你还可以定义某些 Entity Framework 行为。 在此项目中将数据库上下文类命名为SchoolContext

在项目文件夹中,创建名为的文件夹Data。在Data文件夹创建名为SchoolContext.cs的类文件,并将模板代码替换为以下代码:

 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 }
View Code

此代码将为每个实体集创建DbSet属性。 在 Entity Framework 中,实体集通常与数据表相对应,具体实体与表中的行相对应。

在这里可以省略DbSet<Enrollment>DbSet<Course>语句,实现的功能没有任何改变。 Entity Framework 会隐式包含这两个实体因为Student实体引用了Enrollment实体、Enrollment实体引用了Course实体。

当数据库创建完成后, EF 创建一系列数据表,表名默认和DbSet属性名相同。 集合属性的名称一般使用复数形式,但不同的开发人员的命名习惯可能不一样,开发人员根据自己的情况确定是否使用复数形式。在最后一个 DbSet 属性之后添加以下代码,对 DbContext 指定单数的表明来覆盖默认的表名。

1         protected override void OnModelCreating(ModelBuilder modelBuilder)
2         {
3             modelBuilder.Entity<Course>().ToTable("Course");
4             modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
5             modelBuilder.Entity<Student>().ToTable("Student");
6         }
View Code

用依赖注入注册上下文

ASP.NET Core 默认实现依赖注入。在应用程序启动过程通过依赖注入注册相关服务 (例如 EF 数据库上下文)。 需要这些服务的组件 (如 MVC 控制器) 可以通过向构造函数添加相关参数来获得对应服务。

若要将SchoolContext注册为一种服务,打开Startup.cs,并将添加高亮代码添加到ConfigureServices方法中。

1 public void ConfigureServices(IServiceCollection services)
2 {
3     services.AddDbContext<SchoolContext>(options =>
4         options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
5 
6     services.AddMvc();
7 }
View Code

通过调用DbContextOptionsBuilder中的一个方法将数据库连接字符串在配置文件中的名称传递给上下文对象。 进行本地开发时, ASP.NET Core 配置系统appsettings.json文件中读取数据库连接字符串。

添加using语句引用ContosoUniversity.DataMicrosoft.EntityFrameworkCore命名空间,然后生成项目。

打开appsettings.json文件并添加连接字符串,如下所示。

 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 LocalDB 数据库。 LocalDB 是 SQL Server Express 数据库引擎的轻量级版本,用于应用程序开发,不在生产环境中使用。 LocalDB 作为按需启动并在用户模式下运行的轻量级数据库没有复杂的配置。 默认情况下, LocalDB 在C:/Users/<user>目录下创建.mdf数据库文件。

用测试数据初始化数据库

Entity Framework 已经为你创建了一个空数据库。在本部分中编写一个方法用于向数据库填充测试数据,该方法会在数据库创建完成之后执行。

此处将使用EnsureCreated方法来自动创建数据库。 在Data文件夹中,创建名为的新类文件DbInitializer.cs并且将模板代码替换为以下代码,使得在需要时能创建数据库并向其填充测试数据。

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

这段代码首先检查是否有学生数据在数据库中,如果没有的话,就可以假定数据库是新建的,然后使用测试数据进行填充。代码中使用数组存放测试数据而不是使用List<T>集合是为了优化性能。

Program.cs,修改Main方法,使得在应用程序启动时能执行以下操作:

  • 从依赖注入容器中获取数据库上下文实例。
  • 调用 seed 方法,将上下文传递给它。
  • Seed 方法完成此操作时释放上下文。
 1 using ContosoUniversity.Data;
 2 using Microsoft.AspNetCore;
 3 using Microsoft.AspNetCore.Hosting;
 4 using Microsoft.Extensions.DependencyInjection;
 5 using Microsoft.Extensions.Logging;
 6 using System;
 7 
 8 namespace ContosoUniversity
 9 {
10     public class Program
11     {
12         public static void Main(string[] args)
13         {
14             var host = CreateWebHostBuilder(args).Build();
15             using (var scope = host.Services.CreateScope())
16             {
17                 var services = scope.ServiceProvider;
18                 try
19                 {
20                     var context = services.GetRequiredService<SchoolContext>();
21                     DbInitializer.Initialize(context);
22                 }
23                 catch (Exception ex)
24                 {
25                     var logger = services.GetRequiredService<ILogger<Program>>();
26                     logger.LogError(ex, "An error occurred while seeding the database.");
27                 }
28             }
29             host.Run();
30         }
31 
32         public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
33             WebHost.CreateDefaultBuilder(args)
34                 .UseStartup<Startup>();
35     }
36 }
View Code

现在首次运行该应用程序,创建数据库并使用测试数据作为种子数据。 每当你更改你的数据模型,你可以删除数据库、 更新你的 Initialize 方法,然后使用上述方式更新新数据库。 在之后的教程中,你将了解如何在数据模型更改时,只需修改数据库而无需删除重建数据库。

猜你喜欢

转载自www.cnblogs.com/gangle/p/9190287.html