Ćwiczenie — interakcja z danymi
W poprzednim ćwiczeniu utworzono klasy jednostek i kontekst bazy danych. Następnie użyto migracji platformy EF Core do utworzenia schematu bazy danych.
W tym ćwiczeniu ukończysz implementację PizzaService
. Usługa używa programu EF Core do wykonywania operacji CRUD w bazie danych.
Kod operacji CRUD
Aby zakończyć implementację PizzaService
, wykonaj następujące kroki w obszarze Usługi\PizzaService.cs:
Wprowadź następujące zmiany, jak pokazano w przykładzie:
- Dodaj dyrektywę
using ContosoPizza.Data;
. - Dodaj dyrektywę
using Microsoft.EntityFrameworkCore;
. - Dodaj pole na poziomie klasy przed
PizzaContext
konstruktorem. - Zmień sygnaturę
PizzaContext
metody konstruktora, aby zaakceptować parametr. - Zmień kod metody konstruktora, aby przypisać parametr do pola.
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 /// ... }
Wywołanie
AddSqlite
metody dodane do Program.cs wcześniej zarejestrowanePizzaContext
w celu wstrzyknięcia zależności. Po utworzeniuPizzaService
PizzaContext
wystąpienia element jest wstrzykiwany do konstruktora.- Dodaj dyrektywę
Zastąp metodę
GetAll
poniższym kodem:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
Powyższy kod:
- Kolekcja
Pizzas
zawiera wszystkie wiersze w tabeli pizzas. - Metoda
AsNoTracking
rozszerzenia instruuje program EF Core, aby wyłączyć śledzenie zmian. Ponieważ ta operacja jest tylko do odczytu,AsNoTracking
może zoptymalizować wydajność. - Wszystkie pizze są zwracane z
ToList
.
- Kolekcja
Zastąp metodę
GetById
poniższym kodem:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
Powyższy kod:
- Metoda
Include
rozszerzenia przyjmuje wyrażenie lambda, aby określić, żeToppings
właściwości nawigacji iSauce
mają być uwzględnione w wyniku przy użyciu chętnego ładowania. Bez tego wyrażenia program EF Core zwracanull
dla tych właściwości. - Metoda
SingleOrDefault
zwraca pizzę zgodną z wyrażeniem lambda.- Jeśli rekordy nie są zgodne,
null
zwracana jest wartość . - W przypadku dopasowania wielu rekordów zgłaszany jest wyjątek.
- Wyrażenie lambda opisuje rekordy, w których
Id
właściwość jest równa parametrowiid
.
- Jeśli rekordy nie są zgodne,
- Metoda
Zastąp metodę
Create
poniższym kodem:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
Powyższy kod:
newPizza
przyjmuje się, że jest prawidłowym obiektem. Program EF Core nie wykonuje walidacji danych, więc środowisko uruchomieniowe ASP.NET Core lub kod użytkownika muszą obsługiwać każdą walidację.- Metoda
Add
dodajenewPizza
jednostkę do grafu obiektów programu EF Core. - Metoda
SaveChanges
instruuje program EF Core, aby utrwał zmiany obiektu w bazie danych.
Zastąp metodę
AddTopping
poniższym kodem: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(); }
Powyższy kod:
- Odwołania do istniejących
Pizza
obiektów iTopping
są tworzone przy użyciu poleceniaFind
. - Obiekt
Topping
jest dodawany do kolekcjiPizza.Toppings
za pomocą.Add
metody . Zostanie utworzona nowa kolekcja, jeśli nie istnieje. - Metoda
SaveChanges
instruuje program EF Core, aby utrwał zmiany obiektu w bazie danych.
- Odwołania do istniejących
Zastąp metodę
UpdateSauce
poniższym kodem: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(); }
Powyższy kod:
- Odwołania do istniejących
Pizza
obiektów iSauce
są tworzone przy użyciu poleceniaFind
.Find
jest zoptymalizowaną metodą wykonywania zapytań o rekordy według ich klucza podstawowego.Find
najpierw przeszukuje wykres jednostki lokalnej przed wykonaniem zapytania do bazy danych. - Właściwość
Pizza.Sauce
jest ustawiona naSauce
obiekt . - Wywołanie
Update
metody jest niepotrzebne, ponieważ program EF Core wykrywa, że właściwość jest ustawianaSauce
na .Pizza
- Metoda
SaveChanges
instruuje program EF Core, aby utrwał zmiany obiektu w bazie danych.
- Odwołania do istniejących
Zastąp metodę
DeleteById
poniższym kodem:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
Powyższy kod:
- Metoda
Find
pobiera pizzę przy użyciu klucza podstawowego (w tym przypadku).Id
- Metoda
Remove
usuwapizzaToDelete
jednostkę na wykresie obiektów programu EF Core. - Metoda
SaveChanges
instruuje program EF Core, aby utrwał zmiany obiektu w bazie danych.
- Metoda
Zapisz wszystkie zmiany i uruchom polecenie
dotnet build
. Napraw wszelkie występujące błędy.
Inicjowanie bazy danych
Wykonano kod operacji CRUD dla PizzaService
elementu , ale łatwiej jest przetestować operację Odczyt , jeśli baza danych zawiera dobre dane. Decydujesz się zmodyfikować aplikację w celu zainicjowania bazy danych podczas uruchamiania.
Ostrzeżenie
Ten kod rozmieszczania bazy danych nie uwzględnia warunków wyścigu, dlatego należy zachować ostrożność podczas korzystania z niej w środowisku rozproszonym bez ograniczania zmian.
W folderze Dane dodaj nowy plik o nazwie DbInitializer.cs.
Dodaj następujący kod do pliku 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(); } } }
Powyższy kod:
- Klasa
DbInitializer
iInitialize
metoda są definiowane jakostatic
. Initialize
PizzaContext
akceptuje obiekt jako parametr.- Jeśli nie ma żadnych rekordów w żadnej z trzech tabel,
Pizza
tworzone są obiekty ,Sauce
iTopping
. - Obiekty
Pizza
(i ichSauce
Topping
właściwości nawigacji) są dodawane do grafu obiektów przy użyciu poleceniaAddRange
. - Zmiany grafu obiektów są zatwierdzane w bazie danych przy użyciu polecenia
SaveChanges
.
- Klasa
Klasa DbInitializer
jest gotowa do inicjowania bazy danych, ale musi być wywoływana z Program.cs. Poniższe kroki tworzą metodę rozszerzenia, IHost
która wywołuje DbInitializer.Initialize
metodę :
W folderze Dane dodaj nowy plik o nazwie Extensions.cs.
Dodaj następujący kod do pliku 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); } } } }
Powyższy kod:
Metoda
CreateDbIfNotExists
jest definiowana jako rozszerzenieIHost
klasy .Zostanie utworzone odwołanie do
PizzaContext
usługi.Upewnij się, że utworzono gwarantuje, że baza danych istnieje.
Ważne
Jeśli baza danych nie istnieje,
EnsureCreated
tworzy nową bazę danych. Nowa baza danych nie jest skonfigurowana na potrzeby migracji, dlatego należy zachować ostrożność przy użyciu tej metody.Wywoływana
DbIntializer.Initialize
jest metoda . ObiektPizzaContext
jest przekazywany jako parametr.
Na koniec w Program.cs zastąp
// Add the CreateDbIfNotExists method call
komentarz następującym kodem, aby wywołać nową metodę rozszerzenia:app.CreateDbIfNotExists();
Ten kod wywołuje metodę rozszerzenia zdefiniowaną wcześniej przy każdym uruchomieniu aplikacji.
Zapisz wszystkie zmiany i uruchom polecenie
dotnet build
.
Napisałeś cały kod, który musisz wykonać podstawowe operacje CRUD i zainicjować bazę danych podczas uruchamiania. W następnym ćwiczeniu przetestujesz te operacje w aplikacji.