Capítol 5: Crear i publicar una API web a l’Azure
Havent establert que les dades de l'aplicació dels tècnics s'haurien d'obtenir d'origen de sistemes existents a través d'una API web, la Maria i la Kiana treballen conjuntament per determinar exactament quina informació es necessita i en quin format. La Kiana crearà una aplicació web que exposi l'API web adequada i disposarà que s'allotgi a l'Azure. L'aplicació pot connectar-se a l'Azure des de qualsevol indret on hi hagi una connexió sense fil.
Definir les operacions de l'API web: administració d'inventari de camps
A la pantalla Navega de la secció Administració d'inventari de camps de l'aplicació es visualitza una llista de les peces per a les calderes i els sistemes d'aire condicionat (anomenada simplement peces de calderes). La pantalla Detalls permet al tècnic visualitzar més informació sobre una peça seleccionada.
A la base de dades d'inventari existent (anomenada InventoryDB), la informació sobre les peces es troba en una única taula anomenada BoilerParts. La Kiana determina que l'API web hauria de donar suport a les sol·licituds següents:
- Obtenir totes les peces de caldera.
- Obtenir els detalls d'una peça, a partir de l'ID de peça.
Definir les operacions de l'API web: Knowledge Base de camps
Al sistema existent, la base de dades de la Knowledge Base (anomenada KnowledgeDB) conté tres taules que registren i administren les relacions entre consells, enginyers i peces:
- Consells conté els detalls d'un consell. Cada consell consisteix en un resum d'una sola línia que identifica un problema concret (el tema) i una explicació més detallada que descriu com es resol el problema (el cos). Cada consell també fa referència a una peça i a l'enginyer que ha enregistrat el consell.
- BoilerParts, que conté una llista de les peces a què fan referència els consells. Els detalls de les peces s'emmagatzemen a la taula BoilerParts a la base de dades InventoryDB.
- Engineers, on s'enumeren els tècnics que han creat cada consell.
La part de base de coneixements de l'aplicació actualment només conté una pantalla de Navegador de contenidors. La Maria vol implementar la funcionalitat següent:
El tècnic especifica un terme de cerca a la pantalla Navega per trobar tots els consells que coincideixen. La coincidència podria estar en el nom de la peça a la qual fa referència el consell, el text del tema o el cos del consell, o el nom d'un tècnic que és un expert en un equipament específic.
Quan s'han trobat tots els consells coincidents, el tècnic pot seleccionar un consell per veure'n els detalls.
Un tècnic també pot afegir consells nous a la Knowledge Base, així com afegir notes i comentaris a consells existents.
La Knowledge Base és gran i en creixement, i la consulta en diverses taules i columnes pot implicar lògica complexa que requereix una important capacitat de càlcul. Per reduir la càrrega a l'API web, la Kiana utilitza la Cerca cognitiva de l'Azure per proporcionar la funcionalitat de cerca, com s'ha descrit abans. Per donar suport a l'aplicació, la Kiana decideix que calen les operacions següents des de l'API web:
Cercar els detalls d'un consell de la Knowledge Base especificat des de la taula Consells.
Actualitzar un consell existent de la Knowledge Base a la taula Consells.
Afegir un consell nou a la Knowledge Base a la taula Consells, que també pot implicar l'addició de files a les taules BoilerParts i Engineers si la peça especificada o l'enginyer actualment no tenen consells registrats sobre ells. L'assesorament que realment duu a terme la lògica darrere d'afegir un consell nou s'implementarà com una aplicació lògica cridada des del Power Apps.
Definir les operacions de l'API web: planificació de camp
Planificar cites de tècnics requereix no només consultar, afegir i suprimir cites, sinó també registrar informació sobre els clients. El sistema de cites existent registra aquestes dades en tres taules a la base de dades SchedulesDB:
- Cites, que conté els detalls de cada cita, incloent-hi la data, l'hora, el problema, les notes i el tècnic assignat a la tasca.
- Clients, que conté els detalls de cada client, incloent-hi el nom, l'adreça i les dades de contacte.
- Enginyers, on es mostra la llista de cada tècnic que assistirà a les cites.
Nota
La base de dades conté en realitat una quarta taula anomenada AppointmentsStatus. Aquesta taula conté una llista de valors vàlids per a l'estat d'una cita i és simplement una cerca utilitzada per altres parts del sistema de cites existent.
La Kiana decideix que les operacions següents podrien ser útils per a la part de planificació de camp de l'aplicació:
- Cercar totes les cites d'un tècnic especificat.
- Cercar totes les cites del dia actual d'un tècnic especificat.
- Cercar la cita planificada següent d'un tècnic especificat.
- Actualitzar els detalls d'una cita, com ara l'addició de notes o una fotografia.
- Cercar detalls sobre un client.
Cfreació de l'API web: administració de l'inventari de camp
Els sistemes existents emmagatzemen dades mitjançant la base de dades SQL de l'Azure. La Kiana decideix crear l'API web utilitzant el Nucli del marc d'entitats, perquè aquest mètode pot generar gran part del codi de les consultes, insercions i actualitzacions de dades automàticament. La plantilla de l'API web proporcionada per Microsoft també pot crear les descripcions de Swagger que descriuen cada operació de l'API. Aquestes descripcions són útils per provar les operacions de l'API. Moltes eines poden utilitzar aquesta informació per integrar l'API amb altres serveis, com ara l'Administració de l'API de l'Azure.
La Kiana ha començat amb la funcionalitat d'inventari de camp perquè aquesta és la part més fàcil. Les operacions d'inventari de camp a la consulta de l'API web en una única taula, BoilerParts, a la base de dades InventoryDB. Aquesta taula conté les columnes que es mostren a la imatge següent.

La Kiana ha pres l'enfocament del "codi primer" per crear l'API web. Amb aquesta estratègia, fa les accions següents:
Defineix la seva pròpia classe de model C# que reflecteix l'estructura de la taula BoilerParts a la base de dades InventoryDB.
Crea una classe de context del marc d'entitat que utilitza l'API web per connectar-se a la base de dades, per fer-hi consultes.
Configura la classe de context per connectar-se a la base de dades InventoryDB a l'Azure.
Utilitza les eines de la línia d'ordres del Marc d'entitats per generar una classe de controlador de l'API web que implementa sol·licituds HTTP REST per a cadascuna de les operacions que es poden dur a terme amb la taula BoilerParts.
Utilitza l'API Swagger per provar l'API web.
A la imatge següent es mostra l'estructura de nivell alt de l'API web.

La Kiana ha utilitzat el procediment següent per crear l'API web mitjançant eines de línia d'ordres .NET 5.0 i Visual Studio Code:
Obre una finestra de terminal al Visual Studio Code.

Executa l'ordre següent per crear un projecte d'API web nou anomenat FieldEngineeringApi.
dotnet new webapi -o FieldEngineerApiObrr la carpeta FieldEnginerApi.

Suprimeix el controlador WeatherForecastController.cs d'exemple i el fitxer de classe WeatherForecast.cs creat per la plantilla de l'API web.

A la finestra Terminal, afegeix els següents paquets i eines de Marc d'entitat, juntament amb la compatibilitat amb l'ús de l'SQL Server, al projecte.
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-codegeneratorA la carpeta FieldEngineerApi, crea una carpeta nova anomenada Models.

A la carpeta Models, crea un fitxer de codi C# anomenat BoilerPart.cs.

En aquest fitxer, afegeix les propietats i els camps següents. Aquestes propietats i camps reflexen l'estructura de la taula BoilerParts a la base de dades InventoryDB.
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; } } }A la carpeta Models, crea un altre fitxer de codi C# anomenat InventoryContext.cs. Afegeix el codi següent a aquesta classe. La classe proporciona la connexió entre el controlador (que es crearà a continuació) i la base de dades.
using Microsoft.EntityFrameworkCore; namespace FieldEngineerApi.Models { public class InventoryContext : DbContext { public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { } public DbSet\<BoilerPart\> BoilerParts { get; set; } } }Edita el fitxer appsettings.Development.json per al projecte i afegeix una secció ConnectionStrings amb la següent cadena de connexió InventoryDB. Substitueix <server name> pel nom del servidor de la base de dades SQL creat per contenir la base de dades InventoryDB.
{ "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" } } }Important
Només per a aquesta guia, la cadena de connexió conté l'identificador i la contrasenya d'usuari de la base de dades. En un sistema de producció, aquests elements no s'han d'emmagatzemar en text clar en un fitxer de configuració.
Editeu el fitxer Startup.cs i afegiu les directives d'ús següents a la llista a l'inici del fitxer.
using FieldEngineerApi.Models; using Microsoft.EntityFrameworkCore;A la classe Inici, cerqueu el mètode ConfigureServices. Afegiu la declaració següent a aquest mètode.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>(options => options.UseSqlServer(Configuration.GetConnectionString("InventoryDB"))); services.AddControllers(); ... }Modifiqueu el mètode Configura i habiliteu la interfície d'usuari de Swagger encara que l'aplicació s'estigui executant en mode de producció, com es mostra (aquest canvi consisteix a reubicar les dues crides de mètode app.UseSwagger fora de la declaració if).
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")); ... }Important
Aquest canvi permet exposar l'extrem de Swagger per a la integració de l'Administració d'API. Quan Administració d'API s'ha configurat, haureu de tornar a moure aquest codi a dins la sentència if i tornar a implementar l'API web. No deixeu que l'extrem Swagger s'obri en un sistema de producció.
A la finestra Terminal, executeu l'ordre següent per generar el controlador BoilerParts des de la classe de model BoilerPart i la classe de context InventoryContext.
dotnet aspnet-codegenerator controller ^ -name BoilerPartsController -async -api ^ -m BoilerPart -dc InventoryContext -outDir ControllersEl controlador BoilerParts s'ha de crear a la carpeta Controladors.
[!NOTE] El caràcter de terminador de línia, ^, només el reconeix Windows. Si executeu el Visual Studio Code en un sistema Linux, utilitzeu el caràcter \.
Obriu el fitxer BoilerParts.cs de la carpeta Controladors i reviseu el seu contingut. La classe BoilerPartsController exposa els mètodes REST següents:
- GetBoilerParts(), que retorna una llista de tots els objectes de BoilerPart de la base de dades.
- GetBoilerPart(long id), que recupera els detalls de la peça de la caldera especificada.
- PutBoilerPart(long id, BoilerPart boilerPart), que actualitza una peça de caldera a la base de dades amb els detalls de l'objecte BoilerPart especificat com a paràmetre.
- PostBoilerPart(BoilerPart boilerPart), que crea una peça de caldera nova.
- DeleteBoilerPart(long id), que elimina la peça de caldera especificada de la base de dades.
Nota
L'aplicació del tècnic només necessita els dos mètodes Get, però la resta són útils per a l'aplicació de gestió d'inventaris d'escriptori (no es cobreix en aquesta guia).
Compileu i creeu l'API web.
dotnet build
L'API web s'hauria de crear sense generar informes d'errors ni advertiments.
Implementar l'API web a l'Azure: administració d'inventari de camp
La Kiana ha implementat i provat l'API web, realitzant les tasques següents:
Mitjançant l'extensió del compte de l'Azure al Visual Studio Code, inicieu la sessió a la vostra subscripció de l'Azure.
Des de la finestra Terminal del Visual Studio Code, creeu un nou grup de recursos anomenat webapi_rg a la vostra subscripció de l'Azure. A l'ordre següent, substituïu <location> per la regió més propera de l'Azure.
az group create ^ --name webapi_rg ^ --location <location>Creeu un pla de servei de l'aplicació de l'Azure per proporcionar els recursos per allotjar l'API web.
az appservice plan create ^ --name webapi_plan ^ --resource-group webapi_rg ^ --sku F1Nota
F1 és l'SKU gratuït per als plans de servei de l'aplicació. Proporciona un rendiment i una capacitat limitades, i només és apta per al desenvolupament.
Creeu una aplicació web de l'Azure utilitzant el pla de servei de l'aplicació. Substituïu <webapp name> per un nom únic per a l'aplicació web.
az webapp create ^ --name <webapp name> ^ --resource-group webapi_rg ^ --plan webapi_planAl Visual Studio Code, editeu el fitxer appSettings.json i afegiu la mateixa cadena de connexió que heu fet prèviament al fitxer appSettings.Development.json. Recordeu substituir <server name> pel nom del servidor de la base de dades SQL creat per contenir la base de dades InventoryDB.
{ "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": "*" }A la finestra Terminal, empaqueteu l'API web preparada per implementar-la a l'Azure.
dotnet publish -c Release -o ./publishAquesta ordre desa els fitxers empaquetats en una carpeta anomenada publish.
Al Visual Studio Code, feu clic amb el botó dret a la carpeta de publicació i, a continuació, seleccioneu Implementa a l'aplicació web.

Seleccioneu el nom de l'aplicació web que heu creat abans al pas 4 (<webapp name>). A l'exemple següent, l'aplicació web s'anomena my-fieldengineer-webapp.

A la sol·licitud del quadre del Visual Studio Code, seleccioneu Implementa per acceptar l'advertiment i implementar l'aplicació web.

Verifiqueu que l'aplicació web s'implementi correctament i navegueu al lloc web.

El lloc web s'obrirà en una finestra nova del navegador, però es mostrarà un error d'HTTP 404 (no trobat). Això es deu a que les operacions de l'API web estan disponibles a través de l'extrem de l'api en comptes de l'arrel del lloc web. Canvieu l'adreça URL per https://<webapp name>.azurewebsites.net/api/BoilerParts. Aquest URI invoca el mètode GetBoilerParts al controlador BoilerParts. L'API web hauria de respondre amb un document JSON que mostra totes les peces de la base de dades InventoryDB.

Canvieu l'adreça URL del navegador per https://<webapp name>.azurewebsites.net/swagger. Shauria de mostrar l'API de Swagger. Es tracta d'una interfície gràfica d'usuari que permet a un desenvolupador verificar i provar cadascuna de les operacions en una API web. També actua com una eina de documentació útil.

Seleccioneu GET al costat de l'extrem /api/BoilerParts/{id} i, a continuació, seleccioneu Proveu-ho.

Al camp id, introduïu l'ID d'una peça i, a continuació, seleccioneu Executa. Aquesta acció crida el mètode GetBoilerPart(long id) al controlador BoilerParts. Es retornarà un document JSON amb els detalls de la peça o un error HTTP 404 si no es troba cap peça coincident a la base de dades.

Tanqueu el navegador web i torneu al Visual Studio Code.
Crear i implementar l'API web: KnowledgeBase de camp
Les operacions de la Knowledge Base de camp de l'API web funcionen en tres taules a la base de dades del KnowledgeDB: Tips, BoilerParts i Engineers. A la imatge següent es mostren les relacions entre aquestes taules i les columnes que contenen.

La Kiana va adoptar un mètode similar per a la base de dades de la Knowledge Base de camp que el que va utilitzar per a la base de dades d'administració d'inventari de camp. Va realitzar les tasques següents:
Crear classes de model C# que reflecteixen l'estructura de la taula Tips, BoilerParts i Engineers a la base de dades de la KnowledgeDB. El codi de cadascuna d'aquestes classes es mostra a continuació.
Nota
La taula BoilerParts de la base de dades KnowledgeDB és diferent de la taula BoilerParts de la base de dades InventoryDB. Per evitar un xoc de noms, les classes de models de les taules de la base de dades KnowledgeDB tenen el prefix 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; } } }Nota
L'ID d'enginyer és una cadena, no un nombre. Això es deu a que els sistemes existents utilitzen GUID per a identificar els tècnics i altres usuaris.
// 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; } } }Creeu una classe de context del marc d'entitat que utilitza l'API web per connectar-se a la base de dades KnowledgeBase.
// 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; } } }Editeu el fitxer appsettings.Development.json per al projecte i afegiu la cadena de connexió KnowledgeDB següent a la secció ConnectionStrings. Substituïu <server name> pel nom del servidor de la base de dades SQL creat per contenir la base de dades KnowledgeDB.
{ "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": { ... } } }Important
Només per a aquesta guia, la cadena de connexió conté l'identificador i la contrasenya d'usuari de la base de dades. En un sistema de producció, aquests elements no s'han d'emmagatzemar en text clar en un fitxer de configuració.
Editeu el fitxer Startup.cs i, al mètode ConfigureServices, afegiu les declaracions següents.
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(); ... }La segona sentència controla la manera com es serialitzen les dades quan es recuperen. Algunes de les classes de models tenen referències a altres classes de models, que al seu torn poden fer referència a altres classes de models. Algunes d'aquestes referències poden tenir com a conseqüència activitats recursives (l'entitat A fa referència a l'entitat B, que torna a fer referència a l'entitat B, i així successivament). L'opció ReferenceLoopHandling fa que la serialitzador ignori aquest tipus de bucles a les dades, i només retorna una entitat i els objectes que hi fan referència immediatament, però no més.
A la finestra Terminal, executeu l'ordre següent per generar controladors des de les classes de models KnowledgeBaseBoilerTip, KnowledgeBaseTextilerPart i KnowledgeBaseEntexter i la classe de context 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 ControllersTots tres controladors s'han de crear a la carpeta Controladors.
Editeu el fitxer KnowledgeBaseBoilerPartController.cs. Aquest fitxer conté el codi del controlador knowledgeBaseBailerPart. Hauria de seguir el mateix patró que la classe BoilerPartsController creada abans, exposant els mètodes REST que permeten a un client fer una llista, consultar, inserir, actualitzar i suprimir entitats. Afegiu el següent mètode GetTipsForPart al controlador.
[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(); } ... }Aquest mètode retorna tots els suggeriments de la Knowledge Base que fan referència a una part especificada. Fa una consulta a la taula Suggeriments de la base de dades a través de l'objecte KnowledgeBaseContext per trobar aquesta informació.
Editeu el fitxer KnowledgeBaseEngineerController.cs i afegiu el mètode següent a la classe KnowledgeBaseEnbaseerController.
[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(); } ... }El mètode GetTipsForEngineer troba tots els suggeriments de la Knowledge Base publicats per l'enginyer especificat.
A la finestra Terminal, compileu i creeu l'API web.
dotnet buildL'API web s'hauria de crear sense generar informes d'errors ni advertiments.
Editeu el fitxer appSettings.json i afegiu la cadena de connexió de la base de dades KnowledgeDB. Aquesta cadena hauria de ser la mateixa que prèviament vau escriure al fitxer 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": "*" }A la finestra Terminal, empaqueteu l'API web preparada per implementar-la a l'Azure.
dotnet publish -c Release -o ./publishAl Visual Studio Code, feu clic amb el botó dret a la carpeta de publicació i, a continuació, seleccioneu Implementa a l'aplicació web. Implementeu-la a la mateixa aplicació web de l'Azure que heu creat anteriorment. Permeteu que l'auxiliar sobreescrigui l'aplicació web existent amb el codi nou.
Quan la implementació estigui completada, aneu al lloc web, però canvieu l'adreça URL del navegador per https://<webapp name>.azurewebsites.net/swagger. Les operacions per als controladors KnowledgeBaseBoilerPart, KnowledgeBaseEngineer i KnowbasegeBaseTip s'han d'enumerar a més a les operacions existents de BoilerParts. Verifiqueu que les operacions de KnowledgeBaseBoilerPart incloguin una operació GET per a l'URI /api/KnowledgeBaseBoilerPart/{id}/Tips, i les operacions knowledgeBaseEngineer inclouen una operació GET per a l'URI /api/KnowledgeBaseEnpúblicaer/{id}/Tips.

Crear i implementar l'API web: planificació de camp
Les operacions de planificació de camp utilitzen les taules Cites, AppointmentStatuses (es tracta d'una taula de cerca simple que mostra els valors d'estat de les cites vàlides), Clients i Enginyers, que es mostra a la imatge següent. Aquestes taules s'emmagatzemen a la base de dades SchedulesDB.

Per crear les operacions de l'API web per a la part de planificació de camp del sistema, la Kiana filtra les tasques següents:
Crear classes de model C# que reflecteixen l'estructura de la taula AppointmentStatus, Cites, Clients i Enginyers a la base de dades SchedulesDB. El codi següent mostra cadascuna d'aquestes classes.
Nota
La classe de models de la taula Enginyers s'anomena ScheduleEngineer per distingir-la del model de la taula Enginyers a la base de dades 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; } } }Creeu una classe context del marc d'entitat que utilitzi l'API web per connectar-se a la base de dades 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; } } }Editeu el fitxer appsettings.Development.json per al projecte i afegiu la cadena de connexió SchedulesDB següent a la secció ConnectionStrings. Substituïu <server name> pel nom del servidor de la base de dades SQL creat per contenir la base de dades KnowledgeDB.
{ "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": { ... } } }Editeu el fitxer Startup.cs i, al mètode ConfigureServices, afegiu la declaració següent.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>...; services.AddDbContex\<KnowledgeBaseContext>...; services.AddDbContext<ScheduleContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SchedulesDB"))); services.AddControllers().AddNewtonsoftJson(...); ... }A la finestra Terminal, executeu l'ordre següent per generar controladors des de les classes de models Appointment, Customer i ScheduleEngineer i la classe de context ScheduleContext.
Nota
No creeu un controlador diferent per al model 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 ControllersEditeu el fitxer AppointmentsController.cs. A la classe AppointmentsController, cerqueu el mètode GetAppointments. Modifiqueu la declaració de retorn, tal com es mostra. Aquest canvi garanteix que la informació de Customer, Engineer i AppointmentStatus es recuperi com a part de l'operació GET; aquests camps fan referència a altres entitats que altrament es deixarien nuls a causa del mecanisme de càrrega lent del marc de treball d'entitats.
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(); } ... }Al mateix fitxer, modifiqueu el mètode GetAppointment(long id) tal com es mostra.
// 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; }Aquesta versió del mètode emplena els camps Customer, Engineer i AppointmentStatus d'una cita quan es recupera (la càrrega lenta deixaria aquests camps buits altrament).
Trobeu el mètode PutAppointment i substituïu-lo amb el codi següent. Aquesta versió del mètode PutAppointment pren els camps d'una cita que un usuari pot modificar a l'aplicació en comptes d'un objecte de Cita.
[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; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!AppointmentExists(id)) { return NotFound(); } else { throw; } } return NoContent(); }Nota
Com a regla general, les operacions PUT només han de modificar les dades que un usuari hauria de permetre actualitzar, no necessàriament tots els camps d'una entitat.
Obriu el fitxer ScheduleEngineerController.cs i afegiu el mètode GetScheduleEngineerAppointments següent a la classe 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(); } ... }El mètode GetAppointments troba totes les cites per al client especificat. El mètode GetNotes recupera totes les notes que el tècnic ha fet en les visites anteriors al client.
Editeu el fitxer appSettings.json i afegiu la cadena de connexió de la base de dades KnowledgeDB. Aquesta cadena hauria de ser la mateixa que prèviament vau escriure al fitxer 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": "*" }A la finestra Terminal, compileu i creeu l'API web.
dotnet buildL'API web s'hauria de crear sense generar informes d'errors ni advertiments.
A la finestra Terminal, empaqueteu l'API web preparada per implementar-la a l'Azure.
dotnet publish -c Release -o ./publishAl Visual Studio Code, feu clic amb el botó dret a la carpeta de publicació i, a continuació, seleccioneu Implementa a l'aplicació web. Implementeu-la a la mateixa aplicació web de l'Azure que heu creat anteriorment. Permeteu que l'auxiliar sobreescrigui l'aplicació web existent amb el codi nou.
Quan la implementació estigui completada, aneu al lloc web, però canvieu l'adreça URL del navegador per https://<webapp name>.azurewebsites.net/swagger. Verifiqueu que hi ha disponibles les operacions per als controladors de Cites, Client i ScheduleEngineer.
L'API web està ara a punt per incorporar-se a l'aplicació.