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 s databází.
Kódování operací CRUD
Implementaci PizzaService
dokončíte provedením následujících kroků v souboru Services\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 signaturu metody konstruktoru
PizzaContext
tak, aby přijímal parametr. - Změňte kód metody konstruktoru a přiřaďte k poli parametr .
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 souboru Program.cs dříve, bylo zaregistrovánoPizzaContext
pro injektáž závislostí. Při vytvořeníPizzaService
instance se do konstruktoruPizzaContext
vloží .- 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 pizzy. - Rozšiřující
AsNoTracking
metoda dává EF Core pokyn, aby zakázal sledování změn. Vzhledem k tomu, že tato operace je jen pro čtení,AsNoTracking
může optimalizovat výkon. - Všechny pizzy se vrátí 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:
- Rozšiřující
Include
metoda používá výraz lambda k určení, žeToppings
vlastnosti navigace aSauce
mají být zahrnuty do výsledku pomocí dychtivého načítání. Bez tohoto výrazu VRÁTÍ EF Core pro tyto vlastnosti hodnotu null. - Metoda
SingleOrDefault
vrátí pizzu, která odpovídá výrazu lambda.- Pokud se neshoduje žádný záznam,
null
vrátí se hodnota . - Pokud se shoduje více záznamů, vyvolá se výjimka.
- Výraz lambda popisuje záznamy, ve
Id
kterých se vlastnost rovná parametruid
.
- Pokud se neshoduje žádný záznam,
- Rozšiřující
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
se předpokládá, že je platným objektem. EF Core neprovádí ověřování dat, takže jakékoli ověření musí zpracovávat modul runtime ASP.NET Core nebo uživatelský kód.- Metoda
Add
přidá entitunewPizza
do grafu objektu EF Core. - Metoda
SaveChanges
dává EF Core pokyn, aby změny objektu zůstaly zachovány v databázi.
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
objekty aSauce
se vytvářejí pomocíFind
.Find
je optimalizovaná metoda pro dotazování záznamů podle primárního klíče.Find
nejprve prohledá graf místních entit před dotazem na databázi. - Vlastnost
Pizza.Sauce
je nastavenaSauce
na objekt . - Volání
Update
metody není nutné, protože EF Core zjistí, že jste nastaviliSauce
vlastnost proPizza
. - Metoda
SaveChanges
dává EF Core pokyn, aby změny objektu zůstaly zachovány v databázi.
- Odkazy na existující
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
objekty aTopping
se vytvářejí pomocíFind
. - Objekt
Topping
se přidá doPizza.Toppings
kolekce pomocí.Add
metody . Nová kolekce se vytvoří, pokud neexistuje. - Metoda
SaveChanges
dává EF Core pokyn, aby změny objektu zůstaly zachovány 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 podle primárního klíče (v tomto případě).Id
- Metoda
Remove
odebere entitupizzaToDelete
v grafu objektů EF Core. - Metoda
SaveChanges
dává EF Core pokyn, aby změny objektu zůstaly zachovány v databázi.
- Metoda
Uložte všechny změny a spusťte
dotnet build
příkaz . Opravte všechny chyby, ke kterým dochází.
Dosadíte databázi.
Naprogramovali jste operace CRUD pro PizzaService
, ale operaci čtení je jednodušší otestovat, pokud databáze obsahuje dobrá data. Rozhodnete se aplikaci upravit tak, aby se databáze při spuštění dosadila.
Upozornění
Tento kód pro dosílání databáze nebere v úvahu konflikty časování, takže při jeho používání v distribuovaném prostředí buďte opatrní, aniž byste zmírnili změny.
Do složky Data přidejte nový soubor s názvem DbInitializer.cs.
Do souboru 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 v žádné ze tří tabulek nejsou žádné záznamy,
Pizza
vytvoří se objekty ,Sauce
aTopping
. - Objekty
Pizza
(a jejichSauce
aTopping
navigační vlastnosti) se přidají do grafu objektů pomocíAddRange
příkazu . - Změny grafu objektu se potvrdí do databáze pomocí
SaveChanges
příkazu .
- Třída
Třída DbInitializer
je připravená k dosadí databázi, ale je potřeba ji volat z souboru Program.cs. Následující postup vytvoří rozšiřující metodu pro IHost
volání DbInitializer.Initialize
metody :
Do složky Data přidejte nový soubor s názvem Extensions.cs.
Do souboru 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 . Objekt jePizzaContext
předán jako parametr.
Nakonec v souboru Program.cs nahraďte
// Add the CreateDbIfNotExists method call
komentář následujícím kódem pro volání nové rozšiřující metody:app.CreateDbIfNotExists();
Tento kód volá rozšiřující metodu, kterou jste definovali dříve při každém spuštění aplikace.
Uložte všechny změny a spusťte
dotnet build
příkaz .
Napsali jste veškerý kód, který potřebujete k základním operacím CRUD, a dosadili jste databázi při spuštění. V dalším cvičení tyto operace otestujete v aplikaci.