Övning – Interagera med data
I föregående övning skapade du entitetsklasser och en databaskontext. Sedan använde du EF Core-migreringar för att skapa databasschemat.
I den här övningen slutför du implementeringen PizzaService
. Tjänsten använder EF Core för att utföra CRUD-åtgärder i databasen.
Koda CRUD-åtgärderna
Slutför implementeringen PizzaService
genom att utföra följande steg i Services\PizzaService.cs:
Gör följande ändringar enligt exemplet:
- Lägg till ett
using ContosoPizza.Data;
direktiv. - Lägg till ett
using Microsoft.EntityFrameworkCore;
direktiv. - Lägg till ett fält på klassnivå för
PizzaContext
före konstruktorn. - Ändra konstruktorns metodsignatur så att den accepterar en
PizzaContext
parameter. - Ändra konstruktorns metodkod för att tilldela parametern till fältet.
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 /// ... }
Metodanropet
AddSqlite
som du lade till i Program.cs tidigare registreratPizzaContext
för beroendeinmatning. När instansenPizzaService
skapas matas enPizzaContext
in i konstruktorn.- Lägg till ett
Ersätt metoden
GetAll
med följande kod:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
I koden ovan:
- Samlingen
Pizzas
innehåller alla rader i pizzatabellen. - Tilläggsmetoden
AsNoTracking
instruerar EF Core att inaktivera ändringsspårning. Eftersom den här åtgärden är skrivskyddadAsNoTracking
kan du optimera prestandan. - Alla pizzor returneras med
ToList
.
- Samlingen
Ersätt metoden
GetById
med följande kod:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
I koden ovan:
- Tilläggsmetoden
Include
tar ett lambda-uttryck för att ange att navigeringsegenskapernaToppings
ochSauce
ska inkluderas i resultatet med hjälp av ivrig inläsning. Utan det här uttrycket returnerarnull
EF Core för dessa egenskaper. - Metoden
SingleOrDefault
returnerar en pizza som matchar lambda-uttrycket.- Om inga poster matchar
null
returneras. - Om flera poster matchar genereras ett undantag.
- Lambda-uttrycket beskriver poster där egenskapen
Id
är lika med parameternid
.
- Om inga poster matchar
- Tilläggsmetoden
Ersätt metoden
Create
med följande kod:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
I koden ovan:
-
newPizza
antas vara ett giltigt objekt. EF Core utför inte dataverifiering, så ASP.NET Core-körningen eller användarkoden måste hantera alla valideringar. - Metoden
Add
lägger till entitetennewPizza
i EF Core-objektdiagrammet. - Metoden
SaveChanges
instruerar EF Core att spara objektändringarna i databasen.
-
Ersätt metoden
AddTopping
med följande kod: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(); }
I koden ovan:
- Referenser till befintliga
Pizza
ochTopping
objekt skapas med hjälpFind
av . - Objektet
Topping
läggs till iPizza.Toppings
samlingen med.Add
metoden . En ny samling skapas om den inte finns. - Metoden
SaveChanges
instruerar EF Core att spara objektändringarna i databasen.
- Referenser till befintliga
Ersätt metoden
UpdateSauce
med följande kod: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(); }
I koden ovan:
- Referenser till befintliga
Pizza
ochSauce
objekt skapas med hjälpFind
av .Find
är en optimerad metod för att fråga poster efter deras primära nyckel.Find
söker i diagrammet för den lokala entiteten först innan den frågar databasen. - Egenskapen
Pizza.Sauce
är inställd på objektetSauce
. - Ett
Update
metodanrop är onödigt eftersom EF Core identifierar att du angerSauce
egenskapen påPizza
. - Metoden
SaveChanges
instruerar EF Core att spara objektändringarna i databasen.
- Referenser till befintliga
Ersätt metoden
DeleteById
med följande kod:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
I koden ovan:
- Metoden
Find
hämtar en pizza med primärnyckeln (vilket i det här fallet ärId
). - Metoden
Remove
tar bort entitetenpizzaToDelete
i EF Cores objektdiagram. - Metoden
SaveChanges
instruerar EF Core att spara objektändringarna i databasen.
- Metoden
Spara alla ändringar och kör
dotnet build
. Åtgärda eventuella fel som inträffar.
Seed databasen
Du har kodat CRUD-åtgärderna för PizzaService
, men det är enklare att testa läsåtgärden om databasen innehåller bra data. Du bestämmer dig för att ändra appen så att den får databasen vid start.
Varning
Den här databassåddkoden tar inte hänsyn till konkurrensförhållanden, så var försiktig när du använder den i en distribuerad miljö utan att minimera ändringarna.
I mappen Data lägger du till en ny fil med namnet DbInitializer.cs.
Lägg till följande kod i Data\DbInitializer.cs:
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(); } } }
I koden ovan:
- Klassen
DbInitializer
ochInitialize
metoden definieras båda somstatic
. -
Initialize
accepterar ettPizzaContext
objekt som en parameter. - Om det inte finns några poster i någon av de tre tabellerna skapas ,
Pizza
Sauce
ochTopping
objekt. - Objekten
Pizza
(och derasSauce
ochTopping
navigeringsegenskaperna) läggs till i objektdiagrammet med hjälpAddRange
av . - Objektdiagramändringarna checkas in i databasen med hjälp
SaveChanges
av .
- Klassen
Klassen DbInitializer
är redo att skicka databasen, men den måste anropas från Program.cs. Följande steg skapar en tilläggsmetod för IHost
som anropar DbInitializer.Initialize
:
I mappen Data lägger du till en ny fil med namnet Extensions.cs.
Lägg till följande kod i Data\Extensions.cs:
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); } } } }
I koden ovan:
Metoden
CreateDbIfNotExists
definieras som ett tillägg avIHost
.En referens till
PizzaContext
tjänsten skapas.Se till attSkapat ser till att databasen finns.
Viktigt!
Om det inte finns
EnsureCreated
någon databas skapar du en ny databas. Den nya databasen är inte konfigurerad för migreringar, så använd den här metoden med försiktighet.Metoden
DbIntializer.Initialize
anropas. ObjektetPizzaContext
skickas som en parameter.
I Program.cs ersätter du slutligen kommentaren
// Add the CreateDbIfNotExists method call
med följande kod för att anropa den nya tilläggsmetoden:app.CreateDbIfNotExists();
Den här koden anropar tilläggsmetoden som du definierade tidigare varje gång appen körs.
Spara alla ändringar och kör
dotnet build
.
Du skrev all kod som du behöver för att utföra grundläggande CRUD-åtgärder och starta databasen. I nästa övning testar du dessa åtgärder i appen.