Cvičení – interakce s daty
V předchozím cvičení jste vytvořili třídy entit a kontext databáze. Pak jste použili migrace EF Core k vytvoření schématu databáze.
V tomto cvičení dokončíte implementaci PizzaService
. Služba používá EF Core k provádění operací CRUD v databázi.
Kódování operací CRUD
Implementaci PizzaService
dokončíte provedením následujících kroků ve službách\PizzaService.cs:
Proveďte následující změny, jak je znázorněno v příkladu:
- Přidání direktivy
using ContosoPizza.Data;
- Přidání direktivy
using Microsoft.EntityFrameworkCore;
- Přidejte pole na úrovni třídy pro
PizzaContext
před konstruktor. - Změňte podpis metody konstruktoru tak, aby přijímal
PizzaContext
parametr. - Změňte kód metody konstruktoru tak, aby se parametr přiřadil k poli.
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 /// ... }
Volání
AddSqlite
metody, které jste přidali do Program.cs dříve zaregistrovanéPizzaContext
pro injektáž závislostí.PizzaService
Při vytvořeníPizzaContext
instance se vloží do konstruktoru.- Přidání direktivy
Nahraďte metodu
GetAll
následujícím kódem:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
V předchozím kódu:
- Kolekce
Pizzas
obsahuje všechny řádky v tabulce pizza. - Metoda
AsNoTracking
rozšíření dává EF Core pokyn k zakázání sledování změn. Vzhledem k tomu, že tato operace je jen pro čtení,AsNoTracking
může optimalizovat výkon. - Všechny pizzy jsou vráceny s
ToList
.
- Kolekce
Nahraďte metodu
GetById
následujícím kódem:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
V předchozím kódu:
- Metoda
Include
rozšíření přebírá výraz lambda k určení, žeToppings
vlastnosti aSauce
navigace mají být zahrnuty do výsledku pomocí dychtivého načtení. Bez tohoto výrazu vrátínull
EF Core tyto vlastnosti. - Metoda
SingleOrDefault
vrátí pizzu, která odpovídá výrazu lambda.- Pokud se žádné záznamy neshoduje,
null
vrátí se. - Pokud se shoduje více záznamů, vyvolá se výjimka.
- Výraz lambda popisuje záznamy, ve kterých
Id
je vlastnost rovna parametruid
.
- Pokud se žádné záznamy neshoduje,
- Metoda
Nahraďte metodu
Create
následujícím kódem:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
V předchozím kódu:
newPizza
předpokládá se, že jde o platný objekt. EF Core neprovádí ověření dat, takže modul runtime ASP.NET Core nebo uživatelský kód musí zpracovat jakékoli ověření.- Metoda
Add
přidá entitunewPizza
do grafu objektu EF Core. - Metoda
SaveChanges
dává EF Core pokyn k zachování změn objektu v databázi.
Nahraďte metodu
AddTopping
následujícím kódem: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(); }
V předchozím kódu:
- Odkazy na existující
Pizza
aTopping
objekty jsou vytvořeny pomocí .Find
- Objekt
Topping
se přidá doPizza.Toppings
kolekce pomocí.Add
metody. Pokud neexistuje, vytvoří se nová kolekce. - Metoda
SaveChanges
dává EF Core pokyn k zachování změn objektu v databázi.
- Odkazy na existující
Nahraďte metodu
UpdateSauce
následujícím kódem: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(); }
V předchozím kódu:
- Odkazy na existující
Pizza
aSauce
objekty jsou vytvořeny pomocí .Find
Find
je optimalizovaná metoda dotazování záznamů podle primárního klíče.Find
nejprve vyhledá graf místní entity před dotazem na databázi. - Vlastnost
Pizza.Sauce
je nastavena naSauce
objekt. - Volání
Update
metody není nutné, protože EF Core zjistí, že jste vlastnost nastaviliSauce
naPizza
. - Metoda
SaveChanges
dává EF Core pokyn k zachování změn objektu v databázi.
- Odkazy na existující
Nahraďte metodu
DeleteById
následujícím kódem:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
V předchozím kódu:
- Metoda
Find
načte pizzu primárním klíčem (což jeId
v tomto případě). - Metoda
Remove
odebere entitupizzaToDelete
v grafu objektu EF Core. - Metoda
SaveChanges
dává EF Core pokyn k zachování změn objektu v databázi.
- Metoda
Uložte všechny provedené změny a spusťte
dotnet build
. Opravte všechny chyby, ke kterým dochází.
Počáteční hodnota databáze
Naprogramovali jste operace CRUD pro PizzaService
, ale je jednodušší otestovat operaci čtení , pokud databáze obsahuje dobrá data. Rozhodnete se upravit aplikaci tak, aby se při spuštění databáze osílala.
Upozorňující
Tento kód počátečního nastavení databáze nebere v úvahu podmínky časování, proto při jeho použití v distribuovaném prostředí buďte opatrní bez zmírnění změn.
Do složky Data přidejte nový soubor s názvem DbInitializer.cs.
Do data\DbInitializer.cs přidejte následující kód:
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(); } } }
V předchozím kódu:
- Třída
DbInitializer
iInitialize
metoda jsou definovány jakostatic
. Initialize
PizzaContext
přijímá objekt jako parametr.- Pokud nejsou žádné záznamy v žádné ze tří tabulek,
Pizza
Sauce
, aTopping
objekty jsou vytvořeny. - Objekty
Pizza
(a jejichSauce
aTopping
navigační vlastnosti) jsou přidány do grafu objektů pomocíAddRange
. - Změny grafu objektu jsou potvrzeny do databáze pomocí
SaveChanges
.
- Třída
Třída je připravená DbInitializer
k vytvoření databáze, ale musí být volána z Program.cs. Následující kroky vytvoří metodu rozšíření pro IHost
tato volání DbInitializer.Initialize
:
Do složky Data přidejte nový soubor s názvem Extensions.cs.
Do data\Extensions.cs přidejte následující kód:
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); } } } }
V předchozím kódu:
Metoda
CreateDbIfNotExists
je definována jako rozšíření .IHost
Vytvoří se odkaz na
PizzaContext
službu.EnsureCreated zajišťuje, že databáze existuje.
Důležité
Pokud databáze neexistuje,
EnsureCreated
vytvoří novou databázi. Nová databáze není nakonfigurovaná pro migrace, proto tuto metodu používejte opatrně.Volá se
DbIntializer.Initialize
metoda. ObjektPizzaContext
se předává jako parametr.
Nakonec v Program.cs nahraďte
// Add the CreateDbIfNotExists method call
komentář následujícím kódem pro volání nové metody rozšíření:app.CreateDbIfNotExists();
Tento kód volá metodu rozšíření, kterou jste definovali dříve při každém spuštění aplikace.
Uložte všechny provedené změny a spusťte
dotnet build
.
Napsali jste veškerý kód, který potřebujete k provádění základních operací CRUD a k počátečnímu spuštění databáze. V dalším cvičení tyto operace otestujete v aplikaci.