Übung: Interagieren mit Daten
In der vorherigen Übung haben Sie Entitätsklassen und einen Datenbankkontext erstellt. Anschließend haben Sie EF Core-Migrationen verwendet, um das Datenbankschema zu erstellen.
In dieser Übung schließen Sie die Implementierung von PizzaService
ab. Der Dienst verwendet EF Core, um CRUD-Vorgänge für die Datenbank auszuführen.
Programmieren der CRUD-Vorgänge
Führen Sie die folgenden Schritte in Services\PizzaService.cs aus, um die Implementierung von PizzaService
abzuschließen:
Nehmen Sie wie im folgenden Beispiel gezeigt die folgenden Änderungen vor:
- Fügen Sie eine
using ContosoPizza.Data;
-Anweisung hinzu. - Fügen Sie eine
using Microsoft.EntityFrameworkCore;
-Anweisung hinzu. - Fügen Sie vor dem Konstruktor ein Feld auf Klassenebene für
PizzaContext
hinzu. - Ändern Sie die Konstruktormethodensignatur so, dass sie einen Parameter
PizzaContext
akzeptiert. - Ändern Sie den Konstruktormethodencode, damit der Parameter dem Feld zugewiesen wird.
using ContosoPizza.Models; using ContosoPizza.Data; using Microsoft.EntityFrameworkCore; namespace ContosoPizza.Services; public class PizzaService { private readonly PizzaContext _context; public PizzaService(PizzaContext context) { _context = context; } /// ... /// CRUD operations removed for brevity /// ... }
Der
AddSqlite
-Methodenaufruf, den Sie Program.cs zuvor hinzugefügt haben, hatPizzaContext
für die Abhängigkeitsinjektion registriert. Nach dem Erstellen derPizzaService
-Instanz wirdPizzaContext
in den Konstruktor eingefügt.- Fügen Sie eine
Ersetzen Sie die
GetAll
-Methode durch folgenden Code:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
Für den Code oben gilt:
- Die Auflistung
Pizzas
enthält alle Zeilen in der Pizzatabelle. - Die
AsNoTracking
-Erweiterungsmethode weist EF Core an, die Änderungsnachverfolgung zu deaktivieren. Da dieser Vorgang schreibgeschützt ist, kannAsNoTracking
die Leistung optimieren. - Alle Pizzas werden mit
ToList
zurückgegeben.
- Die Auflistung
Ersetzen Sie die
GetById
-Methode durch folgenden Code:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
Für den Code oben gilt:
- Die
Include
-Erweiterungsmethode verwendet einen Lambdaausdruck, um anzugeben, dass die NavigationseigenschaftenToppings
undSauce
mittels Eager Loading in das Ergebnis eingeschlossen werden sollen. Ohne diesen Ausdruck gibt EF Core für diese Eigenschaftennull
zurück. - Die
SingleOrDefault
-Methode gibt eine Pizza zurück, die dem Lambdaausdruck entspricht.- Wenn keine Datensätze übereinstimmen, wird
null
zurückgegeben. - Wenn mehrere Datensätze übereinstimmen, wird eine Ausnahme ausgelöst.
- Der Lambdaausdruck beschreibt Datensätze, bei denen die
Id
-Eigenschaft demid
-Parameter entspricht.
- Wenn keine Datensätze übereinstimmen, wird
- Die
Ersetzen Sie die
Create
-Methode durch folgenden Code:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
Für den Code oben gilt:
- Von
newPizza
wird angenommen, dass es sich um ein gültiges Objekt handelt. EF Core nimmt keine Datenüberprüfung vor, daher müssen eventuelle Überprüfungen von der ASP.NET Core-Runtime oder dem Benutzercode durchgeführt werden. - Durch die
Add
-Methode wird dem EF Core-Objektgraphen dienewPizza
-Entität hinzugefügt. - Die
SaveChanges
-Methode weist EF Core an, die Objektänderungen in der Datenbank zu speichern.
- Von
Ersetzen Sie die
AddTopping
-Methode durch folgenden Code:public void AddTopping(int pizzaId, int toppingId) { var pizzaToUpdate = _context.Pizzas.Find(pizzaId); var toppingToAdd = _context.Toppings.Find(toppingId); if (pizzaToUpdate is null || toppingToAdd is null) { throw new InvalidOperationException("Pizza or topping does not exist"); } if(pizzaToUpdate.Toppings is null) { pizzaToUpdate.Toppings = new List<Topping>(); } pizzaToUpdate.Toppings.Add(toppingToAdd); _context.SaveChanges(); }
Für den Code oben gilt:
- Verweise auf vorhandene
Pizza
- undTopping
-Objekte werden mithilfe vonFind
erstellt. - Das
Topping
-Objekt wird derPizza.Toppings
-Sammlung mit der.Add
-Methode hinzugefügt. Die Auflistung wird neu erstellt, falls sie nicht vorhanden ist. - Die
SaveChanges
-Methode weist EF Core an, die Objektänderungen in der Datenbank zu speichern.
- Verweise auf vorhandene
Ersetzen Sie die
UpdateSauce
-Methode durch folgenden Code:public void UpdateSauce(int pizzaId, int sauceId) { var pizzaToUpdate = _context.Pizzas.Find(pizzaId); var sauceToUpdate = _context.Sauces.Find(sauceId); if (pizzaToUpdate is null || sauceToUpdate is null) { throw new InvalidOperationException("Pizza or sauce does not exist"); } pizzaToUpdate.Sauce = sauceToUpdate; _context.SaveChanges(); }
Für den Code oben gilt:
- Verweise auf vorhandene
Pizza
- undSauce
-Objekte werden mithilfe vonFind
erstellt.Find
ist eine optimierte Methode zum Abfragen von Datensätzen nach ihrem Primärschlüssel.Find
durchsucht zunächst den lokalen Entitätsgraphen, bevor die Datenbank abfragt wird. - Die
Pizza.Sauce
-Eigenschaft ist auf dasSauce
-Objekt festgelegt. - Ein
Update
-Methodenaufruf ist nicht erforderlich, weil EF Core erkennt, dass Sie dieSauce
-Eigenschaft aufPizza
festgelegt haben. - Die
SaveChanges
-Methode weist EF Core an, die Objektänderungen in der Datenbank zu speichern.
- Verweise auf vorhandene
Ersetzen Sie die
DeleteById
-Methode durch folgenden Code:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
Für den Code oben gilt:
- Die
Find
-Methode ruft eine Pizza nach dem Primärschlüssel ab (in diesem FallId
). - Durch die
Remove
-Methode wird die EntitätpizzaToDelete
aus dem EF Core-Objektgraphen entfernt. - Die
SaveChanges
-Methode weist EF Core an, die Objektänderungen in der Datenbank zu speichern.
- Die
Speichern Sie alle Änderungen, und führen Sie
dotnet build
aus. Beheben Sie alle auftretenden Fehler.
Ausführen eines Seedings für die Datenbank
Sie haben die CRUD-Vorgänge für PizzaService
programmiert, aber es ist einfacher, den Lesevorgang zu testen, wenn die Datenbank geeignete Daten enthält. Sie ändern die App so, dass beim Start ein Seeding für die Datenbank ausgeführt wird.
Warnung
Dieser Datenbank-Seedingcode berücksichtigt Racebedingung nicht. Achten Sie daher darauf, wenn Sie ihn in einer verteilten Umgebung verwenden, ohne Änderungen zu beschränken.
Fügen Sie im Ordner Daten eine neue Datei mit dem Namen DbInitializer.cs hinzu.
Fügen Sie den folgenden Code in Data/DbInitializer.cs ein:
using ContosoPizza.Models; namespace ContosoPizza.Data { public static class DbInitializer { public static void Initialize(PizzaContext context) { if (context.Pizzas.Any() && context.Toppings.Any() && context.Sauces.Any()) { return; // DB has been seeded } var pepperoniTopping = new Topping { Name = "Pepperoni", Calories = 130 }; var sausageTopping = new Topping { Name = "Sausage", Calories = 100 }; var hamTopping = new Topping { Name = "Ham", Calories = 70 }; var chickenTopping = new Topping { Name = "Chicken", Calories = 50 }; var pineappleTopping = new Topping { Name = "Pineapple", Calories = 75 }; var tomatoSauce = new Sauce { Name = "Tomato", IsVegan = true }; var alfredoSauce = new Sauce { Name = "Alfredo", IsVegan = false }; var pizzas = new Pizza[] { new Pizza { Name = "Meat Lovers", Sauce = tomatoSauce, Toppings = new List<Topping> { pepperoniTopping, sausageTopping, hamTopping, chickenTopping } }, new Pizza { Name = "Hawaiian", Sauce = tomatoSauce, Toppings = new List<Topping> { pineappleTopping, hamTopping } }, new Pizza { Name="Alfredo Chicken", Sauce = alfredoSauce, Toppings = new List<Topping> { chickenTopping } } }; context.Pizzas.AddRange(pizzas); context.SaveChanges(); } } }
Für den Code oben gilt:
- Die
DbInitializer
-Klasse und dieInitialize
-Methode sind beide alsstatic
definiert. Initialize
akzeptiert einPizzaContext
-Objekt als Parameter.- Wenn in keiner der drei Tabellen Datensätze enthalten sind, werden die Objekte
Pizza
,Sauce
undTopping
erstellt. - Die
Pizza
-Objekte (und die zugehörigen NavigationseigenschaftenSauce
undTopping
) werden dem Objektgraphen mithilfe vonAddRange
hinzugefügt. - Die Änderungen am Objektgraphen werden unter Verwendung von
SaveChanges
in der Datenbank committet.
- Die
Die DbInitializer
-Klasse ist bereit, ein Seeding für die Datenbank durchzuführen, muss jedoch in der Datei Program.cs aufgerufen werden. Mit den folgenden Schritten wird eine Erweiterungsmethode für IHost
erstellt, die DbInitializer.Initialize
aufruft:
Fügen Sie im Ordner Daten eine neue Datei mit dem Namen Extensions.cs hinzu.
Fügen Sie den folgenden Code in Data/Extensions.cs ein:
namespace ContosoPizza.Data; public static class Extensions { public static void CreateDbIfNotExists(this IHost host) { { using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; var context = services.GetRequiredService<PizzaContext>(); context.Database.EnsureCreated(); DbInitializer.Initialize(context); } } } }
Für den Code oben gilt:
Die
CreateDbIfNotExists
-Methode wird als Erweiterung vonIHost
definiert.Ein Verweis auf den
PizzaContext
-Dienst wird erstellt.Mit EnsureCreated wird sichergestellt, dass die Datenbank vorhanden ist.
Wichtig
EnsureCreated
erstellt eine neue Datenbank, wenn keine vorhanden ist. Die neue Datenbank ist nicht für Migrationen konfiguriert, verwenden Sie diese Methode daher mit Vorsicht.Die
DbIntializer.Initialize
-Methode wird aufgerufen. DasPizzaContext
-Objekt wird als Parameter übergeben.
Ersetzen Sie abschließend in Program.cs den Kommentar
// Add the CreateDbIfNotExists method call
durch den folgenden Code, um die neue Erweiterungsmethode aufzurufen:app.CreateDbIfNotExists();
Dieser Code ruft bei jeder Ausführung der App die Erweiterungsmethode auf, die Sie zuvor definiert haben.
Speichern Sie alle Änderungen, und führen Sie
dotnet build
aus.
Sie haben den gesamten Code geschrieben, der zum Durchführen grundlegender CRUD-Vorgänge und zum Seeding der Datenbank beim Startvorgang erforderlich ist. In der nächsten Übung testen Sie diese Vorgänge in der App.