5. Kapitel: Erstellen und Veröffentlichen einer Web-API in Azure
Nachdem Maria und Kiana festgelegt haben, dass die Daten für die Techniker-App über eine Web-API aus vorhandenen Systemen stammen sollen, arbeiten sie zusammen, um genau zu bestimmen, welche Informationen in welchem Format benötigt werden. Kiana erstellt dann eine Web-App, die die entsprechende Web-API verfügbar macht, und sorgt dafür, dass sie in Azure gehostet wird. Die App kann von jedem Ort aus, an dem eine drahtlose Verbindung besteht, eine Verbindung zu Azure herstellen.
Definieren der Web-API-Vorgänge: Außendienstbestandsverwaltung
Der Durchsuchen-Bildschirm des Bereichs „Außendienstbestandsverwaltung“ der App zeigt eine Liste der Teile für Kessel und Klimaanlagen an (einfach als Kesselteile bezeichnet). Auf dem Details-Bildschirm kann der Techniker weitere Informationen zu einem ausgewählten Teil anzeigen.
In der vorhandenen Bestandsdatenbank (namens InventoryDB) werden Informationen zu Teilen in einer einzigen Tabelle mit dem Namen BoilerParts gespeichert. Kiana legt fest, dass die Web-API die folgenden Anforderungen unterstützen soll:
- Alle Kesselteile abrufen.
- Die Details eines Teils anhand der Teile-ID abrufen.
Definieren der Web-API-Vorgänge: Außendienstwissensdatenbank
Im vorhandenen System enthält die Wissensdatenbank (namens KnowledgeDB) drei Tabellen, die die Beziehungen unter Tipps, Technikern und Teilen aufzeichnen und verwalten:
- Tipps, die die Details eines Tipps enthält. Jeder Tipp enthält eine einzeilige Zusammenfassung, die ein bestimmtes Problem identifiziert (der Betreff) und eine detailliertere Erklärung, wie das Problem gelöst werden kann (der Text). Jeder Tipp verweist auch auf ein Teil und den Techniker, der den Tipp aufgezeichnet hat.
- BoilerParts, die eine Liste der Teile enthält, auf die durch Tipps verwiesen wird. Die Details der Teile selbst sind in der BoilerParts-Tabelle in der InventoryDB-Datenbank gespeichert.
- Techniker, in der die Techniker aufgelistet werden, die jeden Tipp verfasst haben.
Der Wissensdatenbankteil der App enthält derzeit nur einen Durchsuchen-Bildschirm als Platzhalter. Maria möchte folgende Funktionalität implementieren:
Der Techniker gibt einen Suchbegriff auf dem Durchsuchen-Bildschirm an, um alle passenden Tipps zu finden. Die Übereinstimmung kann im Namen des Teils liegen, auf den sich der Tipp bezieht, im Text des Betreffs oder des Texts des Tipps oder im Namen eines Technikers, der ein Experte mit einem bestimmten Gerät ist.
Wenn alle passenden Tipps gefunden wurden, kann der Techniker einen Tipp auswählen, um dessen Details anzuzeigen.
Ein Techniker kann der Wissensdatenbank auch neue Tipps hinzufügen sowie vorhandene Tipps mit Notizen und Kommentaren versehen.
Die Wissensdatenbank ist groß und wächst, und das Abfragen über mehrere Tabellen und Spalten hinweg kann komplexe Logik beinhalten, die eine erhebliche Rechenleistung erfordert. Um die Belastung der Web-API zu verringern, verwendet Kiana Azure Cognitive Search, um die zuvor beschriebenen Suchfunktionen bereitzustellen. Um die App zu unterstützen, entscheidet Kiana, dass die folgenden Vorgänge von der Web-API erforderlich sind:
Details zu einem bestimmten Wissensdatenbanktipp finden Sie in der Tipps-Tabelle.
Aktualisieren Sie einen vorhandenen Wissensdatenbanktipp in der Tipps-Tabelle.
Fügen Sie dem einen neuen Wissensdatenbanktipp die Tipps-Tabelle hinzu, was auch das Hinzufügen von Zeilen zu den BoilerParts- und Techniker-Tabellen beinhalten kann, wenn für das angegebene Teil oder den angegebenen Techniker derzeit keine Tipps aufgezeichnet wurden. Die Routine, die tatsächlich die Logik für das Hinzufügen eines neuen Tipps ausführt, wird als Logik-App implementiert, von der aus Power Apps aufgerufen wird.
Definieren der Web-API-Vorgänge: Außendienstplanung
Das Planen von Technikerterminen erfordert nicht nur das Abfragen, Hinzufügen und Entfernen von Terminen, sondern auch das Aufzeichnen von Informationen über Kunden. Das bestehende Terminsystem zeichnet diese Daten in drei Tabellen in der SchedulesDB-Datenbank auf:
- Termine: Hier finden Sie die Details zu jedem Termin, einschließlich Datum, Uhrzeit, Problem, Notizen und Techniker, die der Aufgabe zugewiesen sind.
- Kunden: Hier finden Sie die Details jedes Kunden, einschließlich seines Namens, seiner Adresse und seiner Kontaktdaten.
- Techniker: Hier finden Sie jeden Techniker, der an Terminen teilnimmt.
Hinweis
Die Datenbank enthält tatsächlich eine vierte Tabelle mit dem Namen TermineStatus. Diese Tabelle enthält eine Liste gültiger Werte für den Status eines Termins und ist lediglich eine Suche, die von anderen Teilen des vorhandenen Terminsystems verwendet wird.
Kiana entscheidet, dass die folgenden Vorgänge für den Außendienstplanungsteil der App nützlich sind:
- Suchen Sie alle Termine für einen bestimmten Techniker.
- Suchen Sie alle Termine für den aktuellen Tag für einen bestimmten Techniker.
- Suchen Sie den Nächsten geplanten Termin für einen bestimmten Techniker.
- Aktualisieren Sie die Details eines Termins, z. B. das Hinzufügen von Notizen oder eines Fotos.
- Suchen Sie Details zu einem Kunden.
Erstellen der Web-API: Außendienstbestandsverwaltung
Die vorhandenen Systeme speichern Daten mithilfe der Azure SQL-Datenbank. Kiana beschließt, die Web-API mithilfe von Entity Framework Core zu erstellen, da durch diesen Ansatz ein Großteil des Codes generiert werden kann, der Daten automatisch abfragt, einfügt und aktualisiert. Mit der von Microsoft bereitgestellten Web-API-Vorlage können auch die Swagger-Beschreibungen erstellt werden, die die einzelnen Vorgänge in der API beschreiben. Diese Beschreibungen sind zum Testen der API-Operationen nützlich. Viele Tools können diese Informationen verwenden, um die API in andere Dienste zu integrieren, z. B. Azure API Management.
Kiana begann mit der Außendienstbestandsfunktionalität, da dies der einfachste Teil ist. Die Außendienstbestandsvorgänge in der Web-API fragen eine einzelne Tabelle BoilerParts in der InventoryDB-Datenbank ab. Diese Tabelle enthält die im folgenden Bild gezeigten Spalten.
Kiana verfolgte beim Erstellen der Web-API den „Code-First“-Ansatz und ging folgendermaßen vor:
Sie definierte ihre eigene C# Modell-Klasse, die die Struktur der BoilerParts-Tabelle in der InventoryDB-Datenbank widerspiegelte.
Sie erstellte eine Entity Framework-Kontext-Klasse, die die Web-API verwendet, um eine Verbindung zur Datenbank herzustellen und Abfragen durchzuführen.
Sie konfigurierte die Kontextklasse für die Verbindung mit der InventoryDB-Datenbank in Azure.
Sie verwendete die Entity Framework-Befehlszeilentools, um eine Web-API Controller-Klasse zu generieren, die HTTP-REST-Anforderungen für jede der Vorgänge implementiert, die für die BoilerParts-Tabelle ausgeführt werden können.
Sie verwendete die Swagger-API zum Testen der Web-API.
Das folgende Bild zeigt die allgemeine Struktur der Web-API.
Kiana hat das folgende Verfahren verwendet, um die Web-API mit den .NET 6.0-Befehlszeilentools und mit Visual Studio Code zu erstellen:
Öffnen Sie ein Terminal-Fenster in Visual Studio Code.
Führen Sie den folgenden Befehl aus, um ein neues Web-API-Projekt mit dem Namen FieldEngineerApi zu erstellen.
dotnet new webapi -o FieldEngineerApi
Öffnen Sie den FieldEngineerApi-Ordner.
Entfernen Sie den beispielhaften WeatherForecastController.cs-Controller und die WeatherForecast.cs-Klassendatei, die von der Web-API-Vorlage erstellt wurde.
Fügen Sie im Terminal-Fenster dem Projekt die folgenden Entity Framework-Pakete und -Tools zusammen mit der Unterstützung für die Verwendung von SQL Server hinzu.
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
Erstellen Sie im FieldEngineerApi-Ordner einen neuen Ordner mit dem Namen Modelle.
Erstellen Sie im Modelle-Ordner eine C# Codedatei mit dem Namen BoilerPart.cs.
Fügen Sie dieser Datei die folgenden Eigenschaften und Felder hinzu. Diese Eigenschaften und Felder spiegeln die Struktur der BoilerParts-Tabelle in der InventoryDB-Datenbank wieder.
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; } } }
Erstellen Sie im Modelle-Ordner eine andere C# Codedatei mit dem Namen InventoryContext.cs. Fügen Sie dieser Klasse den folgenden Code hinzu. Die Klasse stellt die Verbindung zwischen dem Controller (der als Nächstes erstellt werden soll) und der Datenbank bereit.
using Microsoft.EntityFrameworkCore; namespace FieldEngineerApi.Models { public class InventoryContext : DbContext { public InventoryContext(DbContextOptions<InventoryContext> options) : base(options) { } public DbSet\<BoilerPart\> BoilerParts { get; set; } } }
Bearbeiten Sie die appsettings.Development.json-Datei für das Projekt, und fügen Sie einen ConnectionStrings-Abschnitt mit der folgenden InventoryDB-Verbindungszeichenfolge hinzu. Ersetzen Sie <server name> durch den Namen des SQL-Datenbankservers, den Sie für die Speicherung der InventoryDB-Datenbank erstellt haben.
{ "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" } } }
Wichtig
Nur für die Zwecke dieses Handbuchs enthält die Verbindungszeichenfolge die Benutzer-ID und das Kennwort für die Datenbank. In einem Produktionssystem sollten Sie diese Elemente niemals im Klartext in einer Konfigurationsdatei speichern.
Bearbeiten Sie die Startup.cs-Datei, und fügen Sie die folgenden using-Anweisungen der Liste am Anfang der Datei hinzu.
using FieldEngineerApi.Models; using Microsoft.EntityFrameworkCore;
Suchen Sie in der Startup-Klasse dieConfigureServices-Methode. Fügen Sie dieser Methode die folgende Anweisung hinzu.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>(options => options.UseSqlServer(Configuration.GetConnectionString("InventoryDB"))); services.AddControllers(); ... }
Ändern Sie die Konfigurieren-Methode, und aktivieren Sie die Swagger-Benutzeroberfläche, auch wenn die App wie gezeigt im Produktionsmodus ausgeführt wird (diese Änderung umfasst das Verschieben der beiden App.UseSwagger -Methodenaufrufe außerhalb der if-Anweisung).
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")); ... }
Wichtig
Durch diese Änderung kann der Swagger-Endpunkt für die API-Management-Integration verfügbar gemacht werden. Nachdem die API-Verwaltung konfiguriert wurde, sollten Sie diesen Code wieder in die if-Anweisung verschieben und die Web-API erneut bereitstellen. Lassen Sie den Swagger-Endpunkt in einem Produktionssystem niemals offen.
Führen Sie im Terminal-Fenster den folgenden Befehl aus, um den BoilerParts-Controller aus der BoilerPart-Modellklasse und der InventoryContext-Kontextklasse zu generieren.
dotnet aspnet-codegenerator controller ^ -name BoilerPartsController -async -api ^ -m BoilerPart -dc InventoryContext -outDir Controllers
Der BoilerParts-Controller sollte im Ordner Controller erstellt werden.
[!NOTE] Das Zeilenabschlusszeichen ^ wird nur von Windows erkannt. Wenn Sie Visual Studio Code auf einem Linux-System ausführen, verwenden Sie stattdessen das \-Zeichen.
Öffnen Sie die BoilerParts.cs-Datei im Controllers-Ordner, und überprüfen Sie den Inhalt. Die BoilerPartsController-Klasse macht die folgenden REST-Methoden verfügbar:
- GetBoilerParts(), die eine Liste aller BoilerPart-Objekte aus der Datenbank zurückgibt.
- GetBoilerPart(long id), die die Details des angegebenen Kesselteils abruft.
- PutBoilerPart(long id, BoilerPart boilerPart), die einen Kesselteil in der Datenbank mit den Details im als Parameter angegebenen BoilerPart-Objekt aktualisiert.
- PostBoilerPart(BoilerPart boilerPart), die ein neues Kesselteil erstellt.
- DeleteBoilerPart(long id), die das angegebene Kesselteil aus der Datenbank entfernt.
Hinweis
Die App des Technikers benötigt nur die beiden Abrufen-Methoden, aber die anderen sind nützlich für die Desktop-Bestandsverwaltungs-App (in diesem Handbuch nicht behandelt).
Kompilieren und erstellen Sie die Web-API.
dotnet build
Die Web-API sollte erstellt werden, ohne dass Fehler oder Warnungen gemeldet werden.
Bereitstellen der Web-API in Azure: Außendienstbestandsverwaltung
Kiana stellte die Web-API bereit und testete sie, indem sie die folgenden Aufgaben ausführte:
Melden Sie sich unter Verwendung der Azure-Kontoerweiterung in Visual Studio Code bei Ihrem Azure-Abonnement an.
Erstellen Sie aus dem Terminalfenster in Visual Studio Code eine neue Ressourcengruppe mit dem Namen webapi_rg in Ihrem Azure-Abonnement. Ersetzen Sie im folgenden Befehl <location> durch die nächstgelegene Azure-Region.
az group create ^ --name webapi_rg ^ --location <location>
Erstellen Sie einen Azure App Service-Plan, um die Ressourcen zum Hosten der Web-API bereitzustellen.
az appservice plan create ^ --name webapi_plan ^ --resource-group webapi_rg ^ --sku F1
Hinweis
F1 ist die kostenlose SKU für App Service-Pläne. Sie bietet einen begrenzten Durchsatz und eine begrenzte Kapazität und ist nur für Entwicklungszwecke geeignet.
Erstellen Sie eine Azure-Webanwendung mithilfe des App Service-Plans. Ersetzen SIe <webapp name> durch einen eindeutigen Namen für die Web-App.
az webapp create ^ --name <webapp name> ^ --resource-group webapi_rg ^ --plan webapi_plan
Bearbeiten Sie in Visual Studio Code die appSettings.json-Datei, und fügen Sie dieselbe Verbindungszeichenfolge hinzu, die Sie zuvor in die appSettings.Development.json-Datei geschrieben haben. Denken Sie daran, <server name> durch den Namen des SQL-Datenbankservers zu ersetzen, den Sie für die Speicherung der InventoryDB-Datenbank erstellt haben.
{ "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": "*" }
Packen Sie im Terminalfenster die Web-API, die für die Bereitstellung in Azure bereit ist.
dotnet publish -c Release -o ./publish
Dieser Befehl speichert die gepackten Dateien in einem Ordner mit dem Namen veröffentlichen.
Im Visual Studio Code klicken Sie mit der rechten Maustaste auf den Veröffentlichen-Ordner, und wählen Sie dann In Web App bereitstellen aus.
Wählen Sie den Namen der Web-App aus, die Sie zuvor in Schritt 4 erstellt haben (<webapp name>). Im folgenden Beispiel wird die Web-App als my-fieldengineer-webapp benannt.
Wählen Sie in der Eingabeaufforderung im Visual Studio Codedialog Bereitstellen aus, um die Warnung zu akzeptieren und die Web-App bereitzustellen.
Stellen Sie sicher, dass die Webanwendung erfolgreich bereitgestellt wurde, und navigieren Sie dann zur Website.
Die Website wird in einem neuen Browserfenster geöffnet, zeigt jedoch einen HTTP 404-Fehler an (nicht gefunden). Dies liegt daran, dass die Web-API-Vorgänge über den API-Endpunkt verfügbar sind und nicht über das Stammverzeichnis der Website. Ändern Sie die URL in https://<webapp name>. azurewebsites.net/api/BoilerParts. Diese URI ruft die GetBoilerParts-Methode im BoilerParts-Controller auf. Die Web-API sollte mit einem JSON-Dokument antworten, in dem alle Kesselteile im aufgeführt in der InventoryDB-Datenbank aufgeführt sind.
Ändern Sie die URL im Browser in https://<webapp name>.azurewebsites.net/swagger. Die Swagger-API sollte angezeigt werden. Dies ist eine grafische Benutzeroberfläche, mit der ein Entwickler jeden Vorgang in einer Web-API überprüfen und testen kann. Sie dient auch als nützliches Dokumentationswerkzeug.
Wählen Sie ABRUFEN neben dem /api/BoilerParts/{id}-Endpunkt und dann Testen.
Geben Sie in das Feld ID die ID eines Teils ein, und wählen Sie dann Ausführen aus. Diese Aktion ruft die GetBoilerPart(long id)-Methode im BoilerParts-Controller auf. Es wird ein JSON-Dokument mit den Details des Teils oder ein HTTP 404-Fehler zurückgegeben, wenn kein übereinstimmendes Teil in der Datenbank gefunden wird.
Schließen Sie den Webbrowser, und kehren Sie zu Visual Studio Code zurück.
Erstellen und Bereitstellen der Web-API: Außendienstwissensdatenbank
Die Außendienstwissensdatenbank-Vorgänge in der Web-API funktionieren in drei Tabellen in der KnowledgeDB-Datenbank: Tipps, BoilerParts und Techniker. Das folgende Bild zeigt das Beziehungen unter diesen Tabellen und die darin enthaltenen Spalten.
Kiana verfolgte einen ähnlichen Ansatz für die Außendienstwissensdatenbank, der für die Außendienstbestandsverwaltungs-Datenbank verwendet wurde und Sie führte die folgenden Aufgaben aus.
Sie erstellt C#-Modellklassen, die die Struktur der Tabelle Tipps, BoilerParts und Techniker in der KnowledgeDB-Datenbank spiegeln. Der Code für jede dieser Klassen wird im Folgenden gezeigt.
Hinweis
Die BoilerParts-Tabelle in der KnowledgeDB-Datenbank unterscheidet sich von der BoilerParts-Tabelle in der InventoryDB-Datenbank. Um einen Namenskonflikt zu vermeiden, erhalten die Modellklassen für Tabellen in der KnowledgeDB-Datenbank den KnowledgeBase-Präfix.
// 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; } } }
Hinweis
Die Techniker-Id ist eine Zeichenfolge, keine Zahl. Dies liegt daran, dass die vorhandenen Systeme GUIDs verwenden, um Techniker und andere Benutzer zu identifizieren.
// 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; } } }
Erstellen Sie eine andere Entity Framework-Kontext-Klasse, die die Web-API verwendet, um eine Verbindung mit der KnowledgeDB-Datenbank herzustellen.
// 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; } } }
Bearbeiten Sie die appsettings.Development.json-Datei für das Projekt, und fügen Sie die folgende KnowledgeDB-Verbindungszeichenfolge dem Abschnitt ConnectionStrings-Abschnitt hinzu. Ersetzen Sie <server name> durch den Namen des SQL-Datenbankservers, den Sie für die Speicherung der KnowledgeDB-Datenbank erstellt haben.
{ "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": { ... } } }
Wichtig
Nur für die Zwecke dieses Handbuchs enthält die Verbindungszeichenfolge die Benutzer-ID und das Kennwort für die Datenbank. In einem Produktionssystem sollten Sie diese Elemente niemals im Klartext in einer Konfigurationsdatei speichern.
Bearbeiten Sie die Startup.cs-Datei, und fügen Sie in der ConfigureServices-Methode die folgenden Anweisungen hinzu.
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(); ... }
Die zweite Anweisung steuert die Art und Weise, wie Daten beim Abrufen serialisiert werden. Einige der Modellklassen haben Verweise auf andere Modellklassen, die wiederum auf weitere Modellklassen verweisen können. Einige dieser Verweise können zu rekursiven Schleifen führen (Entität A verweist auf Entität B, die auf Entität A verweist, die erneut auf Entität B verweist usw.). Die ReferenceLoopHandling-Option bewirkt, dass der Serializer solche Schleifen in den Daten ignoriert und nur eine Entität und die Objekte zurückgibt, auf die er sofort verweist, jedoch nicht mehr.
Führen Sie im Terminal-Fenster den folgenden Befehl aus, um Controller aus den Modellklassen KnowledgeBaseBoilerTip, KnowledgeBaseBoilerPart und KnowledgeBaseEngineer und der KnowledgeBaseContext-Kontextklasse zu generieren.
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
Alle drei Controller sollten im Ordner Controller erstellt werden.
Bearbeiten Sie die KnowledgeBaseBoilerPartController.cs-Datei. Diese Datei enthält den Code für den KnowledgeBaseBoilerPart-Controller. Er sollte dem gleichen Muster folgen wie die zuvor erstellte BoilerPartsController-Klasse, die REST-Methoden verfügbar macht, mit denen ein Client Entitäten auflisten, abfragen, einfügen, aktualisieren und löschen kann. Fügen Sie die folgende Methode GetTipsForPart dem Controller hinzu.
[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(); } ... }
Diese Methode gibt alle Wissensdatenbanktipps zurück, die auf ein bestimmtes Teil verweisen. Sie fragt die Tipps-Tabelle in der Datenbank über das KnowledgeBaseContext-Objekt ab, um diese Informationen zu finden.
Bearbeiten Sie die KnowledgeBaseEngineerController.cs-Datei, und fügen Sie der KnowledgeBaseEngineerController-Klasse die folgende Methode hinzu.
[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(); } ... }
Die GetTipsForEngineer-Methode findet alle Wissensdatenbanktipps, die vom angegebenen Techniker veröffentlicht wurden.
Kompilieren und erstellen Sie im Terminal-Fenster die Web-API.
dotnet build
Die Web-API sollte erstellt werden, ohne dass Fehler oder Warnungen gemeldet werden.
Bearbeiten Sie die appSettings.json-Datei und fügen Sie die Verbindungszeichenfolge für die KnowledgeDB-Datenbank hinzu. Diese Zeichenfolge sollte dieselbe sein, die Sie zuvor in die appSettings.Development.json-Datei geschrieben haben.
{ "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": "*" }
Packen Sie im Terminal-Fenster die Web-API, die für die Bereitstellung in Azure bereit ist.
dotnet publish -c Release -o ./publish
Im Visual Studio Code klicken Sie mit der rechten Maustaste auf den Veröffentlichen-Ordner, und wählen Sie dann In Web App bereitstellen aus. Stellen Sie dieselbe Azure-Webanwendung bereit, die Sie zuvor erstellt haben. Ermöglichen Sie dem Assistenten, die vorhandene Webanwendung mit dem neuen Code zu überschreiben.
Navigieren Sie nach Abschluss der Bereitstellung zur Website, ändern Sie jedoch die URL im Browser in https://<webapp name>.azurewebsites.net/swagger. Die Vorgänge für die Controller KnowledgeBaseBoilerPart, KnowledgeBaseEngineer und KnowldgeBaseTip sollten zusätzlich zu den vorhandenen BoilerParts-Vorgängen aufgeführt werden. Stellen Sie sicher, dass die KnowledgeBaseBoilerPart-Vorgänge einenGET-Vorgang für die URI /api/KnowledgeBaseBoilerPart/{id}/Tipps umfassen, und dass die KnowledgeBaseEngineer-Operationen einen GET-Vorgang für die URI /api/KnowledgeBaseEngineer/{id}/Tipps umfasst.
Erstellen und Bereitstellen der Web-API: Außendienstplanung
Die Außendienstplanungsvorgänge verwenden die Tabellen Termine, AppointmentStatuses (dies ist eine einfache Nachschlagetabelle, in der die gültigen Terminstatuswerte aufgeführt sind), Kunden und Techniker, die im folgenden Bild dargestellt werden. Diese Tabellen werden in der SchedulesDB-Datenbank gespeichert.
Um die Web-API-Vorgänge für den Außendienstplanungsteil des Systems zu erstellen, führte Kiana die folgenden Aufgaben aus:
Sie erstellt C#-Modellklassen, die die Struktur der Tabelle AppointmentStatus, Termine, Kunden und Techniker in der SchedulesDB-Datenbank spiegeln. Der folgende Code zeigt jede dieser Klassen.
Hinweis
Die Modellklasse für die Techniker-Tabelle heißt ScheduleEngineer, um sie vom Modell für die Techniker-Tabelle in der InventoryDB-Datenbank zu unterscheiden.
// 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; } } }
Erstellen Sie eine Entity Framework-Kontext-Klasse, die die Web-API verwendet, um eine Verbindung mit der SchedulesDB-Datenbank herzustellen.
// 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; } } }
Bearbeiten Sie die appsettings.Development.json-Datei für das Projekt, und fügen Sie die folgende SchedulesDB-Verbindungszeichenfolge dem Abschnitt ConnectionStrings-Abschnitt hinzu. Ersetzen Sie <server name> durch den Namen des SQL-Datenbankservers, den Sie für die Speicherung der KnowledgeDB-Datenbank erstellt haben.
{ "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": { ... } } }
Bearbeiten Sie die Startup.cs-Datei, und fügen Sie in der ConfigureServices-Methode die folgende Anweisung hinzu.
public void ConfigureServices(IServiceCollection services) { services.AddDbContext<InventoryContext>...; services.AddDbContex\<KnowledgeBaseContext>...; services.AddDbContext<ScheduleContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SchedulesDB"))); services.AddControllers().AddNewtonsoftJson(...); ... }
Führen Sie im Terminal-Fenster den folgenden Befehl aus, um Controller aus den Modellklassen Termin, Kunde und ScheduleEngineer und der ScheduleContext-Kontextklasse zu generieren.
Hinweis
Erstellen Sie keinen separaten Controller für das AppointmentStatus-Modell.
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
Bearbeiten Sie die AppointmentsController.cs-Datei. Suchen Sie in der TermineController-Klasse die GetAppointments-Methode. Modifiziere Sie die Zurückgeben-Anweisung, wie gezeigt. Diese Änderung stellt sicher, dass die Informationen für Kunde, Techniker und AppointmentStatus als Teil des GET-Vorgangs abgerufen werden. Diese Felder verweisen auf andere Entitäten, die aufgrund des verzögerten Lademechanismus des Entity Frameworks sonst null bleiben würden.
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(); } ... }
Ändern Sie in derselben Datei die GetAppointment(long id)-Methode, wie gezeigt.
// 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; }
Diese Version der Methode füllt die Felder Kunde, Techniker und AppointmentStatus eines Termins, wenn er abgerufen wird (verzögertes Laden würde diese Felder sonst leer lassen).
Suchen Sie die PutAppointment-Methode, und ersetzen Sie sie durch den folgenden Code. Diese Version der PutAppointment-Methode verwendet die Felder in einem Termin, die ein Benutzer in der App ändern kann, statt eines vollständigen Termin-Objekts.
[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(); }
Hinweis
In der Regel sollten PUT-Vorgänge nur Daten ändern, die ein Benutzer aktualisieren darf, nicht unbedingt jedes Feld in einer Entität.
Öffnen Sie die ScheduleEngineerController.cs-Datei, und fügen Sie der ScheduleEngineerController-Klasse die GetScheduleEngineerAppointments-Methode hinzu.
[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(); } ... }
Die GetAppointments-Methode sucht alle Termine für den angegebenen Kunden. Die GetNotes-Methode ruft alle Notizen ab, die der Techniker bei früheren Besuchen beim Kunden gemacht hat.
Bearbeiten Sie die appSettings.json-Datei und fügen Sie die Verbindungszeichenfolge für die KnowledgeDB-Datenbank hinzu. Diese Zeichenfolge sollte dieselbe sein, die Sie zuvor in die appSettings.Development.json-Datei geschrieben haben.
{ "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": "*" }
Kompilieren und erstellen Sie im Terminal-Fenster die Web-API.
dotnet build
Die Web-API sollte erstellt werden, ohne dass Fehler oder Warnungen gemeldet werden.
Packen Sie im Terminal-Fenster die Web-API, die für die Bereitstellung in Azure bereit ist.
dotnet publish -c Release -o ./publish
Im Visual Studio Code klicken Sie mit der rechten Maustaste auf den Veröffentlichen-Ordner, und wählen Sie dann In Web App bereitstellen aus. Stellen Sie dieselbe Azure-Webanwendung bereit, die Sie zuvor erstellt haben. Ermöglichen Sie dem Assistenten, die vorhandene Webanwendung mit dem neuen Code zu überschreiben.
Navigieren Sie nach Abschluss der Bereitstellung zur Website, ändern Sie jedoch die URL im Browser in https://<webapp name>.azurewebsites.net/swagger. Stellen Sie sicher, dass die Vorgänge für die Controller Termine, Kunde und ScheduleEngineer jetzt verfügbar sind.
Die Web-API kann jetzt in die App integriert werden.