已确定技术人员应用的数据应该通过 Web API 源自现有系统,Maria 和 Kiana 共同协作确定需要哪些准确信息以及采用哪种格式。 然后,Kiana 将创建一个 Web 应用,用于公开适当的 Web API 并安排在 Azure 中托管它。 应用可以从具有无线连接的任何位置连接到 Azure。
定义 Web API 操作:现场库存管理
应用的现场库存管理部分的
浏览
屏幕显示样本和空调系统的零件列表(简称为
样本零件
)。 通过
详细信息
屏幕,技术人员可以查看有关选定零件的详细信息。
在现有库存数据库(名为
InventoryDB
)中,有关零件的信息保留在名为
BoilerParts
的单个表中。 Kiana 确定 Web API 应支持以下请求:
获取所有样本零件。
根据零件 ID 获取零件的详细信息。
定义 Web API 操作:现场知识库
在现有系统中,知识库数据库(名为
KnowledgeDB
)包含三个表,用于记录和管理提示、工程师和零件之间的关系:
提示
,包含提示的详细信息。 每个提示由用于识别特定问题(
主题
)的单行摘要和用于描述如何解决问题(
正文
)的更详细说明组成。 每个提示还引用零件和记录了该零件的工程师。
BoilerParts
,包含提示引用的零件列表。 零件本身的详细信息存储在
InventoryDB
数据库的
BoilerParts
表中。
工程师
,列出编写了每个提示的技术人员。
应用的知识库部分当前仅包含占位符
浏览器
屏幕。 Maria 想要实现以下功能:
技术人员在
浏览
屏幕上指定搜索词以查找所有匹配的提示。 匹配内容可能是提示所引用零件的名称、提示主题或正文中的文本或精通一种特定设备的技术人员的姓名。
在找到所有匹配提示后,技术人员可以选择一个提示来查看其详细信息。
技术人员还可以向知识库添加新提示,以及向现有提示添加注释和评论。
知识库规模很大并且不断增长,查询多个表和列涉及需要大量计算能力的复杂逻辑。 为了减少 Web API 上的负载,Kiana 决定使用 Azure 认知搜索提供搜索功能,如前面所述。 为了支持该应用,Kiana 确定需要从 Web API 中执行以下操作:
从
提示
表中查找指定知识库提示的详细信息。
在
提示
表中更新现有知识库提示。
将新知识库提示添加到
提示
表,如果指定零件或工程师当前没有针对它们记录的提示,这可能还涉及向
BoilerParts
和
Engineers
表添加行。 在添加新提示之后实际执行逻辑的例程将会作为从 Power Apps 调用的逻辑应用实施。
定义 Web API 操作:现场计划
计划技术人员约会不仅需要查询、添加和删除约会,还需要记录有关客户的信息。 现有约会系统将此数据记录在
SchedulesDB
数据库的三个表中:
约会
,包含每个约会的详细信息,包括分配到任务的日期、时间、问题、注释和技术人员。
客户
,保留每个客户的详细信息,包括其姓名、地址和联系人详细信息。
工程师
,列出参加约会的每个技术人员。
数据库实际包含名为
AppointmentsStatus
的第四个表。 此表包含约会状态的有效值列表,并且是仅供现有约会系统的其他部分使用的查找。
Kiana 确定以下操作将适用于应用的现场计划部分:
查找指定技术人员的所有约会。
查找指定技术人员当天的所有约会。
查找指定技术人员的下一个计划约会。
更新约会的详细信息,例如添加注释或照片。
查找有关客户的详细信息。
生成 Web API:现场库存管理
现有系统使用 Azure SQL 数据库存储数据。 Kiana 确定使用 Entity Framework Core 生成 Web API,因为此方法可以自动生成大量查询、插入和更新数据的代码。 Microsoft 提供的 Web API 模板还可以创建 Swagger 说明,用于描述 API 中的每个操作。 在测试 API 操作时,这些说明很有用。 许多工具可以使用此信息将 API 与其他服务(例如 Azure API 管理)集成。
Kiana 从现场库存功能开始,因为这是最简单的部分。 Web API 中的现场库存操作在
InventoryDB
数据库中查询单个表
BoilerParts
。 此表包含下图中显示的列。
Kiana 采用“代码优先”方法生成 Web API,并执行了以下操作:
定义了其 C#
模型
类,用于镜像
InventoryDB
数据库中
BoilerParts
表的结构。
创建了一个 Web API 用于连接到数据库以执行查询的实体框架
上下文
类。
配置了上下文类以连接到 Azure 中的
InventoryDB
数据库。
使用实体框架命令行工具生成 Web API
控制器
类,用于为针对
BoilerParts
表执行的每个操作实现 HTTP REST 请求。
使用了 Swagger API 以测试 Web API。
下图显示了 Web API 的高级结构。
Kiana 使用了以下过程通过 .NET 6.0 命令栏工具和 Visual Studio Code 创建 Web API:
在 Visual Studio Code 中打开终端窗口。
运行以下命令以创建名为
FieldEngineerApi
的新 Web API 项目。
dotnet new webapi -o FieldEngineerApi
打开 FieldEngineerApi 文件夹。
删除示例 WeatherForecastController.cs 控制器和由 Web API 模板创建的 WeatherForecast.cs 类文件。
在终端窗口中,通过同时支持使用 SQL Server,将以下实体框架包和工具添加到项目。
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design
dotnet add package Microsoft.EntityFrameworkCore.Design
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson
dotnet tool install --global dotnet-ef
dotnet tool install --global dotnet-aspnet-codegenerator
在 FieldEngineerApi 文件夹中,创建名为模型的新文件夹。
在模型文件夹中,创建名为 BoilerPart.cs 的 C# 代码文件。
在此文件中,添加以下属性和字段。 这些属性和字段镜像 InventoryDB 数据库中 BoilerParts 表的结构。
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace FieldEngineerApi.Models
public class BoilerPart
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string CategoryId { get; set; }
[Column(TypeName = "money")]
public decimal Price { get; set; }
public string Overview { get; set; }
public int NumberInStock { get; set; }
public string ImageUrl { get; set; }
在模型文件夹中,创建名为 InventoryContext.cs 的另一个 C# 代码文件。 将以下代码添加到此类。 该类提供控制器(将在接下来创建)和数据库之间的连接。
using Microsoft.EntityFrameworkCore;
namespace FieldEngineerApi.Models
public class InventoryContext : DbContext
public InventoryContext(DbContextOptions<InventoryContext> options)
: base(options)
public DbSet\<BoilerPart\> BoilerParts { get; set; }
编辑项目的 appsettings.Development.json 文件,并添加具有以下 InventoryDB 连接字符串的 ConnectionStrings 部分。 将 <server name> 替换为您创建用于保存 InventoryDB 数据库的 SQL Database 服务器的名称。
"ConnectionStrings": {
"InventoryDB": "Server=tcp*:<server name>*.database.windows.net,1433;Initial Catalog=InventoryDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
仅出于本指南的目的,连接字符串包含数据库的用户 ID 和密码。 在生产系统中,切勿将这些项目以纯文本格式存储在配置文件中。
编辑 Startup.cs 文件,并在文件开头将以下 using 指令添加到列表。
using FieldEngineerApi.Models;
using Microsoft.EntityFrameworkCore;
在启动类中,找到 ConfigureServices 方法。 将以下语句添加到此方法。
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<InventoryContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("InventoryDB")));
services.AddControllers();
修改配置方法并启用 Swagger UI,即使应用在生产模式下运行,如下所示(此更改涉及在 if 语句外部重新定位两个 app.UseSwagger 方法调用)。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "FieldEngineerApi v1"));
此更改为 API 管理集成公开 Swagger 终结点。 配置 API 管理后,应将此代码移回到 if 语句内部并重新部署 Web API。 切勿使 Swagger 终结点在生产系统中保持打开状态。
在终端窗口中,运行以下命令以从 BoilerPart 模型类和 InventoryContext 上下文类中生成 BoilerParts 控制器。
dotnet aspnet-codegenerator controller ^
-name BoilerPartsController -async -api ^
-m BoilerPart -dc InventoryContext -outDir Controllers
BoilerParts 控制器应在 Controllers 文件夹中创建。
[!NOTE]
仅 Windows 可识别行终止符字符 ^。 如果要在 Linux 系统上运行 Visual Studio Code,请改为使用 \ 字符。
在 Controllers 文件夹中打开 BoilerParts.cs 文件并查看其内容。 BoilerPartsController 类公开以下 REST 方法:
GetBoilerParts(),从数据库返回所有 BoilerPart 对象的列表。
GetBoilerPart(long id),检索指定样本零件的详细信息。
PutBoilerPart(long id, BoilerPart boilerPart),使用指定为参数的 BoilerPart 对象中的详细信息更新数据库中的样本零件。
PostBoilerPart(BoilerPart boilerPart),创建新样本零件。
DeleteBoilerPart(long id),从数据库中删除指定样本零件。
技术人员的应用只需要两个获取方法,而其他方法则对桌面库存管理应用有用(本指南中未介绍)。
编译和生成 Web API。
dotnet build
应生成 Web API,而不报告任何错误或警告。
将 Web API 部署到 Azure:现场库存管理
通过执行以下任务,Kiana 部署并测试了 Web API:
使用 Visual Studio Code 中的 Azure 帐户扩展,登录到 Azure 订阅。
从 Visual Studio Code 中的“终端”窗口,在 Azure 订阅中创建名为 webapi_rg 的新资源组。 在以下命令中,将 <location> 替换为最近的 Azure 区域。
az group create ^
--name webapi_rg ^
--location <location>
创建 Azure 应用服务计划,以提供用于托管 Web API 的资源。
az appservice plan create ^
--name webapi_plan ^
--resource-group webapi_rg ^
--sku F1
F1 是应用服务计划的免费 SKU。 它提供有限的吞吐量和产能,并且仅适用于开发目的。
使用应用服务计划创建 Azure Web 应用。 将 <webapp name> 替换为 Web 应用的唯一名称。
az webapp create ^
--name <webapp name> ^
--resource-group webapi_rg ^
--plan webapi_plan
在 Visual Studio Code 中,编辑 appSettings.json 文件,并添加之前写入到 appSettings.Development.json 文件的相同连接字符串。 请记住,将 <server name> 替换为您创建用于保存 InventoryDB 数据库的 SQL Database 服务器的名称。
"ConnectionStrings": {
"InventoryDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=InventoryDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"**
"Logging": {
"LogLevel": {
"Default\: "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
"AllowedHosts": "*"
在“终端”窗口中,打包 Web API 以供部署到 Azure。
dotnet publish -c Release -o ./publish
此命令将打包的文件保存到名为 publish 的文件夹。
在 Visual Studio Code 中,右键单击发布文件夹,然后选择部署到 Web 应用。
选择您之前在步骤 4 中创建的 Web 应用的名称 (<webapp name>)。 在下面的示例中,Web 应用名为 my-fieldengineer-webapp。
在 Visual Studio Code 对话框中的提示符处,选择部署以接受警告并部署 Web 应用。
验证 Web 应用是否已部署成功,然后浏览到网站。
该网站将在新的浏览器窗口中打开,但将显示 HTTP 404 错误(找不到)。 这是因为 Web API 操作通过 api 终结点(而非网站的根)可用。 将 URL 更改为 https://<webapp name>.azurewebsites.net/api/BoilerParts。 此 URI 在 BoilerParts 控制器中调用 GetBoilerParts 方法。 Web API 应响应 JSON 文档,该文档列出 InventoryDB 数据库中的所有样本零件。
将浏览器中的 URL 更改为 https://<webapp name>.azurewebsites.net/swagger。 应显示 Swagger API。 这是一个图形用户界面,使开发人员能够在 Web API 中验证和测试每个操作。 它还充当一个有用的文档工具。
选择 /api/BoilerParts/{id} 终结点旁边的 GET,然后选择试用。
在 ID 字段中,输入零件的 ID,然后选择执行。 此操作在 BoilerParts 控制器中调用 GetBoilerPart(long id) 方法。 如果在数据库中未找到匹配的零件,它将返回包含零件详细信息的 JSON 文档,或返回 HTTP 404 错误。
关闭 Web 浏览器并返回到 Visual Studio Code。
生成和部署 Web API:现场知识库
Web API 中的现场知识库操作可用于 KnowledgeDB 数据库中的三个表:提示、BoilerParts 和 工程师。 下图显示了这些表之间的关系以及它们包含的列。
Kiana 对现场知识库数据库采用她用于现场库存管理数据库的类似方法,并执行了以下任务:
创建 C# 模型类,用于镜像 KnowledgeDB 数据库中 提示、BoilerParts 和 工程师表的结构。 下面显示了每个类的代码。
KnowledgeDB 数据库中的 BoilerParts 表不同于 InventoryDB 数据库中的 BoilerParts 表。 为了避免名称冲突,KnowledgeDB 数据库中的表的模型类具有 KnowledgeBase 前缀。
// KnowledgeBaseTips.cs
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class KnowledgeBaseTip
[Key]
public long Id { get; set; }
public long KnowledgeBaseBoilerPartId { get; set; }
public virtual KnowledgeBaseBoilerPart KnowledgeBaseBoilerPart { get; set; }
public string KnowledgeBaseEngineerId { get; set; }
public virtual KnowledgeBaseEngineer KnowledgeBaseEngineer { get; set; }
public string Subject { get; set; }
public string Body { get; set; }
工程师 ID 是一个字符串,而不是一个数字。 这是因为现有系统使用 GUID 标识技术人员和其他用户。
// KnowledgeBaseBoilerPart.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class KnowledgeBaseBoilerPart
[Key]
public long Id { get; set; }
public string Name { get; set; }
public string Overview { get; set; }
public virtual ICollection<KnowledgeBaseTip> KnowledgeBaseTips { get; set; }
// KnowledgeBaseEngineer.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class KnowledgeBaseEngineer
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string ContactNumber { get; set; }
public virtual ICollection<KnowledgeBaseTip> KnowledgeBaseTips { get; set; }
创建另一个 Web API 用于连接到 KnowledgeDB 数据库的实体框架上下文类。
// KnowledgeBaseContext.cs
using Microsoft.EntityFrameworkCore;
namespace FieldEngineerApi.Models
public class KnowledgeBaseContext : DbContext
public KnowledgeBaseContext(DbContextOptions<KnowledgeBaseContext> options)
: base(options)
public DbSet<KnowledgeBaseBoilerPart> BoilerParts { get; set; }
public DbSet<KnowledgeBaseEngineer> Engineers { get; set; }
public DbSet<KnowledgeBaseTip> Tips { get; set; }
编辑项目的 appsettings.Development.json 文件,并将以下 KnowledgDB 连接字符串添加到 ConnectionStrings 部分。 将 <server name> 替换为您创建用于保存 KnowledgeDB 数据库的 SQL Database 服务器的名称。
"ConnectionStrings": {
"InventoryDB": "Server=tcp:...",
"KnowledgeDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=KnowledgeDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"Logging": {
仅出于本指南的目的,连接字符串包含数据库的用户 ID 和密码。 在生产系统中,切勿将这些项目以纯文本格式存储在配置文件中。
编辑 Startup.cs 文件,然后在 ConfigureServices 方法中,添加以下语句。
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<InventoryContext>...;
services.AddDbContext<KnowledgeBaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("KnowledgeD")));
services.AddControllers().AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore**
services.AddControllers();
第二个语句控制检索数据时数据序列化的方式。 一些模型类包含对其他模型类的引用,从而可以进一步引用模型类。 其中一些引用可能导致递归循环(实体 A 引用实体 B,该实体引用回实体 A,又再次引用实体 B,等等)。 ReferenceLoopHandling 选项导致序列化程序忽略数据中的此类循环,并仅返回实体和它引用的对象,不再返回其他内容。
在终端窗口中,运行以下命令以从 KnowledgeBaseBoilerTip、KnowledgeBaseBoilerPart 和 KnowledgeBaseEngineer 模型类以及 KnowledgeBaseContext 上下文类中生成控制器。
dotnet aspnet-codegenerator controller ^
-name KnowledgeBaseTipController -async -api ^
-m KnowledgeBaseTip ^
-dc KnowledgeBaseContext -outDir Controllers
dotnet aspnet-codegenerator controller ^
-name KnowledgeBaseBoilerPartController -async -api ^
-m KnowledgeBaseBoilerPart ^
-dc KnowledgeBaseContext -outDir Controllers
dotnet aspnet-codegenerator controller ^
-name KnowledgeBaseEngineerController -async -api ^
-m KnowledgeBaseEngineer ^
-dc KnowledgeBaseContext -outDir Controllers
全部三个控制器都应在 Controllers 文件夹中创建。
编辑 KnowledgeBaseBoilerPartController.cs 文件。 此文件包含 KnowledgeBaseBoilerPart 控制器的代码。 它应该遵循与前面创建的 BoilerPartsController 类相同的模式,公开使客户端能够列出、查询、插入、更新和删除实体的 REST 方法。 将以下 GetTipsForPart 方法添加到控制器。
[Route("api/[controller]")]
[ApiController]
public class KnowledgeBaseBoilerPartController : ControllerBase
private readonly KnowledgeBaseContext _context;
public KnowledgeBaseBoilerPartController(KnowledgeBaseContext context)
_context = context;
// GET: api/KnowledgeBaseBoilerPart/5/Tips
[HttpGet("{id}/Tips")]
public async Task<ActionResult<IEnumerable<KnowledgeBaseTip>>>GetTipsForPart(long id)
return await _context.Tips.Where(
t => t.KnowledgeBaseBoilerPartId == id).ToListAsync();
此方法返回引用指定零件的所有知识库提示。 它通过 KnowledgeBaseContext 对象查询数据库中的提示表以查找此信息。
编辑 KnowledgeBaseEngineerController.cs 文件并将以下方法添加到 KnowledgeBaseEngineerController 类。
[Route("api/[controller]")]
[ApiController]
public class KnowledgeBaseEngineerController : ControllerBase
private readonly KnowledgeBaseContext _context;
public KnowledgeBaseEngineerController(KnowledgeBaseContext context)
_context = context;
// GET: api/KnowledgeBaseEngineer/5/Tips
[HttpGet("{id}/Tips")]
public async Task\<ActionResult<IEnumerable<KnowledgeBaseTip>>> GetTipsForEngineer(string id)
return await _context.Tips.Where(t =>
t.KnowledgeBaseEngineerId == id).ToListAsync();
GetTipsForEngineer 方法查找指定工程师发布的所有知识库提示。
在终端窗口中,编译和生成 Web API。
dotnet build
应生成 Web API,而不报告任何错误或警告。
编辑 appSettings.json 文件并为 KnowledgeDB 数据库添加连接字符串。 此字符串应与之前写入到 appSettings.Development.json 文件的字符串相同。
"ConnectionStrings": {
"InventoryDB": ...,
"KnowledgeDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=KnowledgeDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"Logging": {
"AllowedHosts": "*"
在终端窗口中,打包 Web API 以供部署到 Azure。
dotnet publish -c Release -o ./publish
在 Visual Studio Code 中,右键单击发布文件夹,然后选择部署到 Web 应用。 部署到您之前创建的相同 Azure Web 应用。 允许向导使用新代码覆盖现有 Web 应用。
部署完成后,浏览到网站,但将浏览器中的 URL 更改为 https://<webapp name>.azurewebsites.net/swagger。 除了现有的 BoilerParts 操作外,还应该列出 KnowledgeBaseBoilerPart、KnowledgeBaseEngineer 和 KnowldgeBaseTip 控制器的操作。 验证 KnowledgeBaseBoilerPart 操作包括 URI /api/KnowledgeBaseBoilerPart/{id}/Tips 的 GET 操作,KnowledgeBaseEngineer 操作包括 URI /api/KnowledgeBaseEngineer/{id}/Tips 的 GET 操作。
生成和部署 Web API:现场计划
现场计划操作使用表约会、AppointmentStatuses(这是列出有效约会状态值的简单查找表)、客户和工程师,如下图所示。 这些表存储在 SchedulesDB 数据库中。
若要为系统的现场计划部分创建 Web API 操作,Kiana 执行了以下任务:
创建 C# 模型类,用于镜像 SchedulesDB 数据库中 AppointmentStatus、约会、客户和工程师表的结构。 下面的代码显示其中每个列。
工程师表的模型类名为 ScheduleEngineer,以与 InventoryDB 数据库中工程师表的模型区分。
// AppointmentStatus.cs
using Newtonsoft.Json;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class AppointmentStatus {
[Key]
public long Id { get; set; }
public string StatusName { get; set; }
[JsonIgnore]
public virtual ICollection<Appointment> Appointments { get; set; }
// Appointment.cs
using System;
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class Appointment
[Key]
public long Id { get; set; }
[Required]
public long CustomerId { get; set; }
public virtual Customer Customer { get; set; }
public string ProblemDetails { get; set; }
[Required]
public long AppointmentStatusId { get; set; }
public virtual AppointmentStatus AppointmentStatus { get; set; }
public string EngineerId { get; set; }
public virtual ScheduleEngineer Engineer { get ; set; }
[Display(Name = "StartTime")]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy H:mm:ss}")]
public DateTime StartDateTime { get; set; }
public string Notes { get; set; }
public string ImageUrl { get; set; }
// Customer.cs
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace FieldEngineerApi.Models
public class Customer
[Key]
public long Id { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public string ContactNumber { get; set; }
public virtual ICollection<Appointment> Appointments { get; set; }
// ScheduleEngineer.cs
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace FieldEngineerApi.Models
public class ScheduleEngineer
[Key]
public string Id { get; set; }
[Required]
public string Name { get; set; }
public string ContactNumber { get; set; }
[JsonIgnore]
public virtual ICollection<Appointment> Appointments { get; set; }
创建 Web API 用于连接到 SchedulesDB 数据库的实体框架上下文类。
// ScheduleContext.cs
using System;
using Microsoft.EntityFrameworkCore;
namespace FieldEngineerApi.Models
public class ScheduleContext : DbContext
public ScheduleContext(DbContextOptions<ScheduleContext> options)
: base(options)
public DbSet<Appointment> Appointments { get; set; }
public DbSet<AppointmentStatus> AppointmentStatuses { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<ScheduleEngineer> Engineers { get; set; }
编辑项目的 appsettings.Development.json 文件,并将以下 SchedulesDB 连接字符串添加到 ConnectionStrings 部分。 将 <server name> 替换为您创建用于保存 KnowledgeDB 数据库的 SQL Database 服务器的名称。
"ConnectionStrings": {
"InventoryDB": "Server=tcp*: ...",
"KnowledgeDB": "Server=tcp; ... ",
"SchedulesDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=SchedulesDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"Logging": {
编辑 Startup.cs 文件,然后在 ConfigureServices 方法中,添加以下语句。
public void ConfigureServices(IServiceCollection services)
services.AddDbContext<InventoryContext>...;
services.AddDbContex\<KnowledgeBaseContext>...;
services.AddDbContext<ScheduleContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchedulesDB")));
services.AddControllers().AddNewtonsoftJson(...);
在终端窗口中,运行以下命令以从约会、客户和 ScheduleEngineer 模型类和 ScheduleContext 上下文类中生成控制器。
不要为 AppointmentStatus 模型创建单独的控制器。
dotnet aspnet-codegenerator controller ^
-name AppointmentsController -async -api ^
-m Appointment ^
-dc ScheduleContext -outDir Controllers
dotnet aspnet-codegenerator controller ^
-name CustomerController -async -api ^
-m Customer ^
-dc ScheduleContext -outDir Controllers
dotnet aspnet-codegenerator controller ^
-name ScheduleEngineerController -async -api ^
-m ScheduleEngineer ^
-dc ScheduleContext -outDir Controllers
编辑 AppointmentsController.cs 文件。 在 AppointmentsController 类中,查找 GetAppointments 方法。 修改返回语句,如下所示。 此更改确保检索客户、工程师和 AppointmentStatus 信息作为 GET 操作的一部分;这些字段引用由于实体框架的延迟加载机制而保留为 NULL 的其他实体。
public class AppointmentsController : ControllerBase
private readonly ScheduleContext _context;
public AppointmentsController(ScheduleContext context)
_context = context;
// GET: api/Appointments
[HttpGet]
public async Task<ActionResult<IEnumerable<Appointment>>> GetAppointments()
return await _context.Appointments
.Include(c => c.Customer)
.Include(e => e.Engineer)
.Include(s => s.AppointmentStatus)
.ToListAsync();
在相同文件中,修改 GetAppointment(long id) 方法,如下所示。
// GET: api/Appointments/5
[HttpGet("{id}")]
public async Task<ActionResult<Appointment>> GetAppointment(long id)
var appointment = _context.Appointments
.Where(a => a.Id == id)
.Include(c => c.Customer)
.Include(e => e.Engineer)
.Include(s => s.AppointmentStatus);
var appData = await appointment.FirstOrDefaultAsync();
if (appData == null)
return NotFound();
return appData;
在检索约会时,此版本的方法将填充约会的客户、工程师和 AppointmentStatus 字段(否则,延迟加载将使这些字段保留为空)。
查找 PutAppointment 方法,并将其替换为以下代码。 此版本的 PutAppointment 方法获取约会中的字段,用户可以在应用中修改这些字段,而不是完整的约会对象。
[HttpPut("{id}")]
public async Task<IActionResult> PutAppointment(long id,
string problemDetails, string statusName,
string notes, string imageUrl)
var statusId = _context.AppointmentStatuses.First(s =>
s.StatusName == statusName).Id;
var appointment = _context.Appointments.First(e =>
e.Id == id);
if (appointment == null)
return BadRequest();
appointment.ProblemDetails = problemDetails;
appointment.AppointmentStatusId = statusId;
appointment.Notes = notes;
appointment.ImageUrl = imageUrl;
_context.Entry(appointment).State = EntityState.Modified;
await _context.SaveChangesAsync();
catch (DbUpdateConcurrencyException)
if (!AppointmentExists(id))
return NotFound();
throw;
return NoContent();
根据一般规则,PUT 操作仅应该修改应允许用户更新的数据,不一定修改实体中的每个字段。
打开 ScheduleEngineerController.cs 文件,并将以下 GetScheduleEngineerAppointments 方法添加到 ScheduleEngineerController 类。
[Route("api/[controller]")]
[ApiController]
public class ScheduleEngineerController : ControllerBase
private readonly ScheduleContext _context;
public ScheduleEngineerController(ScheduleContext context)
_context = context;
// GET: api/ScheduleEngineer/5/Appointments
[HttpGet("{id}/Appointments")]
public async Task<ActionResult<IEnumerable<Appointment>>> GetScheduleEngineerAppointments(string id)
return await _context.Appointments
.Where(a => a.EngineerId == id)
.OrderByDescending(a => a.StartDateTime)
.Include(c => c.Customer)
.Include(e => e.Engineer)
.Include(s => s.AppointmentStatus)
.ToListAsync();
These methods retrieve the appointments for the specified technician.
Edit the CustomerController.cs file and add the GetAppointments and GetNotes methods, as shown, to the CustomerController class.
[Route("api/[controller]")]
[ApiController]
public class CustomerController : ControllerBase
private readonly ScheduleContext _context;
public CustomerController(ScheduleContext context)
_context = context;
//GET: api/Customers/5/Appointments
[HttpGet("{id}/Appointments")]
public async Task<ActionResult<IEnumerable<Appointment>>> GetAppointments(long id)
return await _context.Appointments
.Where(a => a.CustomerId == id)
.OrderByDescending(a => a.StartDateTime)
.ToListAsync();
//GET: api/Customers/5/Notes
[HttpGet("{id}/Notes")]
public async Task<ActionResult<IEnumerable<object>>> GetNotes(long id)
return await _context.Appointments
.Where(a => a.CustomerId == id)
.OrderByDescending(a => a.StartDateTime)
.Select(a =>
new {a.StartDateTime, a.ProblemDetails, a.Notes})
.ToListAsync();
GetAppointments 方法查找指定客户的所有约会。 GetNotes 方法检索技术人员在之前访问客户时进行的所有注释。
编辑 appSettings.json 文件并为 KnowledgeDB 数据库添加连接字符串。 此字符串应与之前写入到 appSettings.Development.json 文件的字符串相同。
"ConnectionStrings": {
"InventoryDB": ...,
"KnowledgeDB": ...,
"SchedulesDB": "Server=tcp:<server name>.database.windows.net,1433;Initial Catalog=SchedulesDB;Persist Security Info=False;User ID=sqladmin;Password=Pa55w.rd;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
"Logging": {
"AllowedHosts": "*"
在终端窗口中,编译和生成 Web API。
dotnet build
应生成 Web API,而不报告任何错误或警告。
在终端窗口中,打包 Web API 以供部署到 Azure。
dotnet publish -c Release -o ./publish
在 Visual Studio Code 中,右键单击发布文件夹,然后选择部署到 Web 应用。 部署到您之前创建的相同 Azure Web 应用。 允许向导使用新代码覆盖现有 Web 应用。
部署完成后,浏览到网站,但将浏览器中的 URL 更改为 https://<webapp name>.azurewebsites.net/swagger。 验证约会、客户和 ScheduleEngineer 控制器的操作现在可用。
Web API 现在可以合并到应用中。