五、设置DbContext
和控制器
路由和控制器负责接收、验证和处理传入的 HTTP 请求。 在处理过程中,控制器可能通过DbContext
持久化或不持久化并读取数据库中的记录。 了解控制器和DbContext
如何一起工作对于构建预期工作的 web 应用项目至关重要。
本章结束后,您将能够理解以下主题:
- 编写实体和枚举
- 建立数据库 EF Core 和
DbContext
- 写入控制器和路由
- 使用 Swagger UI 测试控制器
技术要求
以下是你完成本章所需要的东西:
- Visual Studio 2019, Visual Studio for Mac,或 Rider
dotnet-ef
工具- SQLite 浏览器或 SQLiteStudio
下面是本章的入门库,也是第 4 章、application Clean Architecture ASP. php 的完整源代码.NET Core Solution:https://github.com/PacktPublishing/ASP.NET-Core-and-Vue.js/tree/master/Chapter05。
写入实体和枚举
在我们开始在域项目中编写实体和枚举之前,下面是它们的快速定义。 什么是实体? 实体是应用中领域对象模型的表示。 数据库将实体转换为数据库表中的一行。 类似地,实体的属性是数据库表中的列。
另一方面,枚举**是类的一种类型,表示一组常量或只读变量。
我们已经快速地了解了实体和 enum 的定义; 现在,让我们看看他们的行动。
为 Travel Tour 应用创建实体和枚举
在您的解决方案应用中,转到Travel.Domain
项目并创建两个目录:
Entities
Enums
Entities
目录将用于所有 Travel Tour 应用实体,而Enums
目录将用于所有 Travel Tour 应用枚举:
-
In the
Entities
folder, create a C# file,TourPackage.cs
, and write this code:namespace Travel.Domain.Entities { public class TourPackage { public int Id { get; set; } public int ListId { get; set; } public string Name { get; set; } public string WhatToExpect { get; set; } public string MapLocation { get; set; } public float Price { get; set; } public int Duration { get; set; } public bool InstantConfirmation { get; set; } } }
类是带有
Travel.Domain.Entities
的namespace
的TourPackage
实体。TourPackage
实体代表包访问数据库中的数据,Id
,ListId
,Name
,WhatToExpect
、【显示】,Price
,Duration
和InstantConfirmation
。 它还需要一个 enum 用于Currency
,一个对象关系用于List
。 现在让我们创建这两个类。 -
In your
Enums
folder, createCurrency.cs
and write the following code:namespace Travel.Domain.Enums { public enum Currency { PHP, USD, JPY, EUR, NOK } }
Currency
枚举文件有Travel.Domain.Enums
的namespace
,枚举有PHP
、USD
、JPY
、EUR
和NOK
。 -
Now switch back to the
TourPackage
class to add theCurrency
enum like so:using Travel.Domain.Enums; namespace Travel.Domain.Entities { public class TourPackage { … public int Duration { get; set; } public bool InstantConfirmation { get; set; } public Currency Currency { get; set; } } }
引入
Travel.Domain.Enums``namespace
,这样您就可以使用刚刚使用Travel.Domain.Enums
创建的Enum
类。 现在在InstantConfirmation
属性之后添加Currency
属性。 -
Next, let's create a
TourList.cs
C# file inside theEntities
folder and write the following code:using System.Collections.Generic; namespace Travel.Domain.Entities { public class TourList { public TourList() { Tours = new List<TourPackage>(); } public IList<TourPackage> Tours { get; set; } public int Id { get; set; } public string City { get; set; } public string Country { get; set; } public string About { get; set; } } }
让我们引入并定义
TourList
实体。TourList
实体有一个构造函数,该构造函数使用新的TourPackage``new List<TourPackage>()
类型初始化Tours
属性,该类型设置一对多关系。 其余属性为Id
、City
、Country
、About
。 -
Now go back to
TourPackage.cs
and add aList
property of typeTourList
below theCurrency
property:using Travel.Domain.Enums; namespace Travel.Domain.Entities { public class TourPackage { … public bool InstantConfirmation { get; set; } public Currency Currency { get; set; } public TourList List { get; set; } } }
List
类型TourList
表示一对一关系,也称为对象-对象关系。
为域项目编写实体和枚举到此结束。 现在让我们转到下一个部分,设置实体框架核心(EF 核心)、DbContext
和我们将在应用中使用的数据库。
建立数据库、EF Core 和 DbContext
您通常需要一个持久化框架,这是一个中间件,如果需要访问应用中的数据库,它可以帮助开发人员将数据存储在数据库中。 使用持久性框架,您可以轻松地使用实体来查询或将对象保存到数据库中。 现在让我们来看看 EF Core 和DbContext
是什么。
英孚核心
在 ASP.NET Core,您可以从头构建持久性框架,但您不必这样做,因为这既耗时又昂贵。 为什么? 编写许多存储过程很难维护; 通过 ADO 读取数据.NET 对象将它们映射到应用中的表是很痛苦的。
所以,这里出现实体框架来拯救。 实体框架是一个持久性框架,它为您完成所有的管道工作。 通常,您不需要编写任何存储过程或将表映射到您的实体。
什么是英孚核心? EF Core是一个跨平台对象关系映射器(O/RM)。 EF Core 本身是实体框架的一个版本,它是一个使用 O/RM 在后台存储和检索对象的持久性框架。
这就是 EF Core 的概述; 让我们前往DbContext
。
DbContext
EF Core 提供了一个名为DbContext
的类,它是我们的数据库的接口。 DbContext
可以有一个或多个DbSet
来表示数据库中的表。 我们使用语言集成查询(LINQ)来查询DbSet
实体,EF Core 会在运行时将我们的 LINQ 查询转换为 SQL 查询。
DbContext
打开到数据库的连接,读取数据并映射到对象,并将其添加到DbContext
的DbSet
。
当我们在DbSet
中保存、更新或删除对象时,EF Core 会跟踪这些更改。 当我们要求持久化更改时,EF Core 自动生成 SQL 语句并在数据库中执行它们。
以上就是对DbContext
的简要概述; 现在是在应用中写入DbContext
和一些DbSet
实体的时候了。
设置
这个设置是将是一个简单的 EF Core 和DbContext
设置。 然而,我们将在此过程中进行重构和改进:
-
First things first, install the
dotnet-ef
tool globally in your Terminal or CMD:dotnet tool install --global dotnet-ef
dotnet-ef
工具是。net CLI 的 EF Core 工具。 您可以访问nuget.org/packages/dotnet-ef链接查看dotnet-ef
工具的最新版本。我们还需要安装两个 NuGet 包。 我们将使用
dotnet cli
,因为它可以在任何操作系统上运行,但如果您更喜欢使用管理 NuGet 包,即 IDE 中的 NuGet 包安装程序 UI,您也可以这样做。 -
Now go to the
Travel.WebApi
project and run this command:dotnet add package Microsoft.EntityFrameworkCore.Design
在运行时使用 EF Core 迁移数据库时需要
Microsoft.EntityFrameworkCore.Design
。 -
Next, go to the
Travel.Data
project and run this command:dotnet add package Microsoft.EntityFrameworkCore.Sqlite
Microsoft.EntityFrameworkCore.Sqlite
是 EF Core 的 SQLite 数据库提供商。 -
Now, still inside the
Data
project, let's writeDbContext
we will need here. Create a folder namedContexts
inside theTravel.Data
project. Inside the folder, create aTravelDbContext.cs
file and write the following code:using Microsoft.EntityFrameworkCore; using Travel.Domain.Entities; namespace Travel.Data.Contexts { public class TravelDbContext : DbContext { public TravelDbContext (DbContextOptions<TravelDbContext> options) : base(options) { } public DbSet<TourList> TourLists { get; set; } public DbSet<TourPackage> TourPackages { get; set; } } }
前面的代码导入了
Microsoft.EntityFrameworkCore
包和Travel.Domain.Entities
包。 我们将类命名为TravelDbContext
,由 EF Core 的DbContext
衍生而来。TraveDbContext
的构造器有一个TravelDbContext
类型的DbContextOptions
参数选项,该选项指向 base 关键字。您将在构造函数下面看到两个
DbSet
实体——TourLists
和TourPackages
。 -
现在转到
Travel.WebApi
项目的Startup.cs
文件,并引入这两个名称空间:using Microsoft.EntityFrameworkCore; using Travel.Data.Contexts;
-
Write the following code inside the
ConfigureServices
method:services.AddDbContext<TravelDbContext>(options => options .UseSqlite("Data Source=TravelTourDatabase.sqlite3"));
代码在
IServiceCollection
中将TravelDbContext
注册为服务,而UseSqlite
将上下文配置为连接到 SQLite 数据库。ConfigureServices
方法应该看起来像这样:public void ConfigureServices(IServiceCollection services) { services.AddDbContext<TravelDbContext>(options => options .UseSqlite("Data Source=TravelTourDatabase.sqlite3")); services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Travel.WebApi", Version = "v1" }); }); }
-
Now let's go back to the
Travel.Data
project for database migration.请注意
所有命令均为一行命令。 书页的宽度会给任何太长的命令增加一行。
-
After navigating to the
Travel.Data
project, you are now ready to run your first migration:dotnet ef migrations add InitialCreate --startup-project ../../presentation/Travel.WebApi/
EF Core 会在你的项目中创建一个名为
Migrations
的文件夹,并生成 c#文件。 这些文件是名为InitialCreate
的迁移构建器和数据库当前模式的快照。 -
Next, create a database and schema from the migration files:
dotnet ef database update --startup-project ../../presentation/Travel.WebApi/
该命令在 Web API 项目中创建一个 SQLite 数据库文件
TravelTourDatabase.sqlite3
。 -
要查看您所做的成功迁移,请使用 SQLiteStudio 或 SQLite Browser。
- 在 SQLiteStudio 或 SQLite 浏览器中添加
TravelTourDatabase
SQLite 数据库文件来查看表:
alicia alicia 工作室
上面 SQLiteStudio 的截图显示TravelTourDatabase
,下面 SQLite Browser 的截图显示TravelTourDatabase
:
图 5.2 - SQLite 浏览器
您已经了解了实体、实体框架、EF Core 和DbContext
是什么。 你也学会了在哪里获得它们以及如何建立它们。 现在我们有了一个数据库,让我们继续下一节,写入控制器和路由。
写入控制器和路由
现在到了部分,我们将编写处理来自客户端应用的 HTTP 请求的控制器。 我们还将编写将 HTTP 请求重定向到其各自控制器的路由。
这将是两个简单的控制器,但我们将在下一章中重构它们,第六章,深入 CQRS。
TourPackagesController
我们将为TourPackages
创建控制器,该控制器将处理任何检索和更新数据库中TourPackages
表的请求:
-
Now go back to the
Travel.WebApi
project and createTourPackagesController.cs
inside of theControllers
directory and write the following code:using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; using Travel.Data.Contexts; using Travel.Domain.Entities;
代码导入前面的
namespaces
,这是此控制器所需要的。 -
Next, write the
controller
class:namespace Travel.WebApi.Controllers { [ApiController] [Route("api/[controller]")] public class TourPackagesController : ControllerBase { } }
我们将使用
ApiController
属性,该属性为装饰类提供一些 API 行为,如 HTTP API 响应,然后是另一个属性Route
—重定向 HTTP 请求的路由。TourPackagesController
类派生自ControllerBase
,一个 MVC 控制器的基类,但不支持视图。 -
Next, write the following code inside
TourPackagesController
:private readonly TravelDbContext _context; public TourPackagesController(TravelDbContext context) { _context = context; }
我们将
TravelDbContext
注入TourPackagesController
的构造函数中,然后将其赋值给后台字段_context
。 现在让我们来看看控制器中的第一个公共方法。 -
Next, we need to write the
Get
method under the constructor method:[HttpGet] public IActionResult Get() { return Ok(_context.TourPackages); }
HttpGet
属性标识一个可以支持HttpGet
方法的操作。 另外,Get
方法返回一个200
状态代码和TourPackage
集合。 在使用GET HTTP
动词请求时,当请求的端点为api/tourpackages
时,此方法将被触发。现在让我们转向下一个公共方法。
-
Next, we need to write the
Create
method under theGet
method:[HttpPost] public async Task<IActionResult> Create([FromBody] TourPackage tourPackage) { await _context.TourPackages.AddAsync (tourPackage); await _context.SaveChangesAsync(); return Ok(tourPackage); }
在
Create
方法之上的HttpPost
属性确定了一个可以支持HttpPost
方法的动作。post
方法采用带有FromBody
装饰的TourPackage
对象,指定应该使用请求体绑定参数。Create
方法返回一个200
状态码以及新创建的TourPackage
。 当使用POST HTTP
动词请求时,请求的端点为api/tourpackages
时,此方法将被触发。你还会注意到这里的类型为
IActionResult
的Task
。IActionResult
是一个接口,它定义了一个契约,表示一个操作方法的结果,而Task
用于异步操作。现在让我们继续下一个公共方法
TourPackagesController
。 -
Next, we write the
Delete
method under theCreate
method:[HttpDelete("{id}")] public async Task<IActionResult> Delete([FromRoute] int id) { var tourPackage = await _context.TourPackages.SingleOrDefaultAsync (tp => tp.Id == id); if (tourPackage == null) { return NotFound(); } _context.TourPackages.Remove(tourPackage); await _context.SaveChangesAsync(); return Ok(tourPackage); }
Delete
方法之上的HttpDelete
属性标识一个可以支持HttpGet
方法的操作。 当使用DELETE HTTP
动词请求时,请求的端点为api/tourpackages/{id}
时,此方法将被触发。您将注意到方法的
HttpDelete
属性中有一个"{id}"
字符串。SingleOrDefaultAsync
使用id
参数,将其与现有对象匹配,然后返回要从表中删除的对象。DbContext
必须调用SaveChangesAsync()
以完成指定TourPackage
对象的删除。最后,
controller
包括200 - OK
应答中删除的TourPackage
。现在我们来看看方法。
-
We need to write the
Update
method under theDelete
method:[HttpPut("{id}")] public async Task<IActionResult> Update([FromRoute] int id, [FromBody] TourPackage tourPackage) { _context.Update(tourPackage); await _context.SaveChangesAsync(); return Ok(tourPackage); }
前面的代码是一个方法,更新一个
TourPackage
对象。
这就是TourPackagesController
。 现在我们可以继续TourListsController
。
TourListsController
现在在Controllers
目录中创建TourListsController
。 TourListsController
与TourPackagesController
具有相同的结构。
下面是TourListsController
代码的摘要。 查看应用的 GitHub repo第 5 章|Finish directory
中的代码,并将其写入您的文件:
using Microsoft.AspNetCore.Mvc;
…
using Travel.Domain.Entities;
namespace Travel.WebApi.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class TourListsController : ControllerBase
{
private readonly TravelDbContext _context;
public TourListsController(TravelDbContext context){…}
[HttpGet]
public IActionResult Get(){…}
[HttpPost]
public async Task<IActionResult> Create([FromBody]
TourList tourList){…}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete([FromRoute]
int id){…}
[HttpPut("{id}")]
public async Task<IActionResult> Update([FromRoute]
int id, [FromBody] TourList tourList){…}
}
}
如您所见,代码也是一个派生自ControllerBase
的类,具有Get
、Create
、Delete
和Update
公共方法。
现在我们已经完成了为应用编写的控制器,让我们运行它来看看我们的工作是如何进行的,通过运行下面的命令仍然在WebApi
项目中:
dotnet run
上述命令将启动或运行应用。 在应用运行时,我们可以继续下一节,使用 Swagger UI测试控制器,以查看控制器的工作情况。
使用 Swagger UI 测试控制器
我们已经构建了控制器并运行了应用,但是还没有测试它,对吧? 在本节中,我们将向TourList
控制器发送GET、POST、PUT和DELETE请求。
首先,确保您正在通过进入Web Api
项目并运行以下命令来运行应用:
dotnet run
现在检查 Swagger UI,在应用运行时转到 https://localhost:5001/swagger/index.html 链接。
您将看到旅游列表和旅游套餐端点。 Swagger UI 向您显示每个控制器的 HTTP 方法的文档。 有GET,POST,DELETE,和PUT方法准备尝试:
图 5.3 - Swagger UI
下面的截图显示了应用的模式,它们也是由 Swagger UI 为你生成的:
图 5.4 - Swagger UI 中的模式
现在让我们尝试一下TourLists
控制器的GET方法。 它将返回一个空的集合或数组,因为我们还没有在其中添加任何数据。
现在转到POST,点击试试按钮。 在单击Execute按钮之前,用以下代码替换请求体:
{
"city": "Oslo",
"country": "Norway",
"about": "Oslo, the capital of Norway, sits on the country's southern coast at the head of the Oslofjord. It's known for its green spaces and museums. Many of these are on the Bygdøy Peninsula, including the waterside Norwegian Maritime Museum and the Viking Ship Museum, with Viking ships from the 9th century. The Holmenkollbakken is a ski-jumping hill with panoramic views of the fjord. It also has a ski museum."
}
你可以复制前面的请求体中,我们将使用发送POST 请求到服务器从Commands.txt
文件,您可以找到在第五章应用文件夹的 GitHub 回购。
我们将只在请求体的文本区域中包含city
属性、country
属性和about
属性,如下所示:
图 5.5 - POST 方法的请求体
在替换了POST部分的默认请求体后,可以看到图 5.5,点击Execute按钮:
图 5.6 - TourLists 控制器的响应体
您可以在图 5.6中看到响应体具有我们发布的属性和1
的id
。
如果您想更新Oslo City
条目或删除它,您可以到 Swagger UI 并在那里发出请求。 但是,由于我们也将在应用中使用它,所以我将保持原样。
你可以在本书的 GitHub 库中查看章节的完整解决方案。
现在我们已经完成了这一部分,让我们回顾一下所学的内容。
总结
作为本章的总结,我们已经介绍了实体在应用中表示域模型,并且我们在应用的域层中编写实体。
我们还介绍了 EF Core,它帮助开发人员在不使用 O/RM 的 SQL 查询的情况下从数据库中持久化和查询数据。
我们了解到 Entity Framework 的DbContext
是数据库的接口,而DbSet
表示数据库中的一个表。
最后,我们用DbContext
编写了两个控制器,并通过通过 Swagger UI 发送 HTTP 请求对它们进行了测试。
现在,您已经掌握了使用 EF Core 处理任何客户端应用发送的任何 HTTP 请求并处理它们并将它们保存在任何类型的关系数据库中的技能。
在下一章中,我们将学习如何使用CQRS或命令和查询职责隔离来编写可维护和可测试的代码。 那么,回头见。**
版权属于:月萌API www.moonapi.com,转载请注明出处