Ö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 som du ser i 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 konstruktormetodens signatur för att acceptera en
PizzaContext
parameter. - Ändra konstruktormetodkoden 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 som tidigare registreratsPizzaContext
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 prestanda. - 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 returnerar EF Core null för dessa egenskaper. - Metoden
SingleOrDefault
returnerar en pizza som matchar lambda-uttrycket.- Om inga poster matchar
null
returneras. - Om flera poster matchar utlöses ett undantag.
- Lambda-uttrycket beskriver poster där
Id
egenskapen ä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å validering måste hanteras av ASP.NET Core körnings- eller användarkod.- Metoden
Add
lägger till entitetennewPizza
i EF Core-objektdiagrammet. - Metoden
SaveChanges
instruerar EF Core att spara objektändringarna i databasen.
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
objekt ochSauce
objekt skapas med hjälpFind
av .Find
är en optimerad metod för att köra frågor mot poster efter deras primärnyckel.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
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
objekt 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
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 ärId
i det här fallet). - 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.
Lägg till en ny fil med namnet DbInitializer.cs i mappen Data.
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
Pizza
skapas ,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
:
Lägg till en ny fil med namnet Extensions.cs i mappen Data.
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 förIHost
.En referens till
PizzaContext
tjänsten skapas.Se till attSkapa säkerställer att databasen finns.
Viktigt
Om det inte finns
EnsureCreated
någon databas skapar du en ny databas. Den nya databasen har inte konfigurerats 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 har skrivit all kod du behöver för att utföra grundläggande CRUD-åtgärder och dirigera databasen vid start. I nästa övning testar du dessa åtgärder i appen.