Oefening: interactie met gegevens
In de vorige oefening hebt u entiteitsklassen en een databasecontext gemaakt. Vervolgens hebt u EF Core-migraties gebruikt om het databaseschema te maken.
In deze oefening voltooit u de PizzaService
implementatie. De service gebruikt EF Core om CRUD-bewerkingen uit te voeren op de database.
Codeer de CRUD-bewerkingen
Voltooi de PizzaService
implementatie door de volgende stappen in Services\PizzaService.cs uit te voeren:
Breng de volgende wijzigingen aan, zoals wordt weergegeven in het voorbeeld:
- Voeg een
using ContosoPizza.Data;
richtlijn toe. - Voeg een
using Microsoft.EntityFrameworkCore;
richtlijn toe. - Voeg een veld op klasseniveau toe voor
PizzaContext
vóór de constructor. - Wijzig de handtekening van de constructormethode om een
PizzaContext
parameter te accepteren. - Wijzig de code van de constructormethode om de parameter toe te wijzen aan het veld.
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 /// ... }
De
AddSqlite
methodeaanroep die u hebt toegevoegd aan Program.cs eerder geregistreerdPizzaContext
voor afhankelijkheidsinjectie. Wanneer hetPizzaService
exemplaar wordt gemaakt, wordt er eenPizzaContext
in de constructor geïnjecteerd.- Voeg een
Vervang de
GetAll
-methode door de volgende code:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
In de voorgaande code:
- De
Pizzas
verzameling bevat alle rijen in de pizzatabel. - Met de
AsNoTracking
extensiemethode wordt EF Core geïnstrueerd om wijzigingen bijhouden uit te schakelen. Omdat deze bewerking alleen-lezen is,AsNoTracking
kunt u de prestaties optimaliseren. - Alle pizza's worden geretourneerd met
ToList
.
- De
Vervang de
GetById
-methode door de volgende code:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
In de voorgaande code:
- De
Include
extensiemethode gebruikt een lambda-expressie om op te geven dat deToppings
eigenschappen enSauce
navigatie-eigenschappen moeten worden opgenomen in het resultaat met behulp van gretig laden. Zonder deze expressie retourneertnull
EF Core deze eigenschappen. - De
SingleOrDefault
methode retourneert een pizza die overeenkomt met de lambda-expressie.- Als er geen records overeenkomen,
null
wordt deze geretourneerd. - Als er meerdere records overeenkomen, wordt er een uitzondering gegenereerd.
- De lambda-expressie beschrijft records waarbij de
Id
eigenschap gelijk is aan deid
parameter.
- Als er geen records overeenkomen,
- De
Vervang de
Create
-methode door de volgende code:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
In de voorgaande code:
newPizza
wordt ervan uitgegaan dat het een geldig object is. EF Core voert geen gegevensvalidatie uit, dus de ASP.NET Core-runtime of -gebruikerscode moet een validatie afhandelen.- De
Add
methode voegt denewPizza
entiteit toe aan de EF Core-objectgrafiek. - Met
SaveChanges
de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
Vervang de
AddTopping
-methode door de volgende 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(); }
In de voorgaande code:
- Verwijzingen naar bestaande
Pizza
enTopping
objecten worden gemaakt met behulp vanFind
. - Het
Topping
object wordt met de.Add
methode aan dePizza.Toppings
verzameling toegevoegd. Er wordt een nieuwe verzameling gemaakt als deze niet bestaat. - Met
SaveChanges
de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
- Verwijzingen naar bestaande
Vervang de
UpdateSauce
-methode door de volgende 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(); }
In de voorgaande code:
- Verwijzingen naar bestaande
Pizza
enSauce
objecten worden gemaakt met behulp vanFind
.Find
is een geoptimaliseerde methode voor het opvragen van records op basis van hun primaire sleutel.Find
doorzoekt eerst de grafiek van de lokale entiteit voordat er een query op de database wordt uitgevoerd. - De
Pizza.Sauce
eigenschap is ingesteld op hetSauce
object. - Een
Update
methodeaanroep is overbodig omdat EF Core detecteert dat u deSauce
eigenschap instelt opPizza
. - Met
SaveChanges
de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
- Verwijzingen naar bestaande
Vervang de
DeleteById
-methode door de volgende code:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
In de voorgaande code:
- Met
Find
de methode wordt een pizza opgehaald op basis van de primaire sleutel (inId
dit geval). - De
Remove
methode verwijdert de entiteit in depizzaToDelete
objectgrafiek van EF Core. - Met
SaveChanges
de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
- Met
Sla al uw wijzigingen op en voer deze uit
dotnet build
. Los eventuele fouten op die optreden.
De database seeden
U hebt de CRUD-bewerkingen gecodeerd voor PizzaService
, maar het is eenvoudiger om de leesbewerking te testen als de database goede gegevens bevat. U besluit de app te wijzigen om de database te seeden bij het opstarten.
Waarschuwing
Deze seeding-code voor databases houdt geen rekening met racevoorwaarden, dus wees voorzichtig bij het gebruik ervan in een gedistribueerde omgeving zonder wijzigingen te beperken.
Voeg in de map Gegevens een nieuw bestand toe met de naam DbInitializer.cs.
Voeg de volgende code toe aan 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(); } } }
In de voorgaande code:
- De
DbInitializer
klasse enInitialize
methode zijn beide gedefinieerd alsstatic
. Initialize
accepteert eenPizzaContext
object als parameter.- Als er geen records in een van de drie tabellen,
Pizza
enSauce
Topping
objecten worden gemaakt. - De
Pizza
objecten (en de bijbehorendeSauce
enTopping
navigatie-eigenschappen) worden aan de objectgrafiek toegevoegd met behulp vanAddRange
. - De wijzigingen in de objectgrafiek worden doorgevoerd in de database met behulp van
SaveChanges
.
- De
De DbInitializer
klasse is klaar om de database te seeden, maar moet worden aangeroepen vanuit Program.cs. Met de volgende stappen maakt u een extensiemethode voor IHost
die aanroepen DbInitializer.Initialize
:
Voeg in de map Gegevens een nieuw bestand toe met de naam Extensions.cs.
Voeg de volgende code toe aan 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); } } } }
In de voorgaande code:
De
CreateDbIfNotExists
methode wordt gedefinieerd als een uitbreiding vanIHost
.Er wordt een verwijzing naar de
PizzaContext
service gemaakt.EnsureCreated zorgt ervoor dat de database bestaat.
Belangrijk
Als er geen database bestaat,
EnsureCreated
maakt u een nieuwe database. De nieuwe database is niet geconfigureerd voor migraties, dus gebruik deze methode met voorzichtigheid.De
DbIntializer.Initialize
methode wordt aangeroepen. HetPizzaContext
object wordt doorgegeven als een parameter.
Vervang ten slotte in Program.cs de
// Add the CreateDbIfNotExists method call
opmerking door de volgende code om de nieuwe extensiemethode aan te roepen:app.CreateDbIfNotExists();
Met deze code wordt de extensiemethode aangeroepen die u eerder hebt gedefinieerd telkens wanneer de app wordt uitgevoerd.
Sla al uw wijzigingen op en voer deze uit
dotnet build
.
U hebt alle code geschreven die u nodig hebt om eenvoudige CRUD-bewerkingen uit te voeren en de database te seeden bij het opstarten. In de volgende oefening test u deze bewerkingen in de app.