Cvičení – interakce s daty

Dokončeno

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:

  1. Proveďte následující změny, jak je znázorněno v příkladu:

    1. Přidání direktivy using ContosoPizza.Data;
    2. Přidání direktivy using Microsoft.EntityFrameworkCore;
    3. Přidejte pole na úrovni třídy pro PizzaContext před konstruktor.
    4. Změňte podpis metody konstruktoru tak, aby přijímal PizzaContext parametr.
    5. 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.

  2. 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.
  3. 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í, že Toppings vlastnosti a Sauce 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 parametru id .
  4. 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á entitu newPizza do grafu objektu EF Core.
    • Metoda SaveChanges dává EF Core pokyn k zachování změn objektu v databázi.
  5. 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 a Topping objekty jsou vytvořeny pomocí .Find
    • Objekt Topping se přidá do Pizza.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.
  6. 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 a Sauce 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 na Sauce objekt.
    • Volání Update metody není nutné, protože EF Core zjistí, že jste vlastnost nastavili Sauce na Pizza.
    • Metoda SaveChanges dává EF Core pokyn k zachování změn objektu v databázi.
  7. 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ž je Id v tomto případě).
    • Metoda Remove odebere entitu pizzaToDelete v grafu objektu EF Core.
    • Metoda SaveChanges dává EF Core pokyn k zachování změn objektu v databázi.
  8. 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.

  1. Do složky Data přidejte nový soubor s názvem DbInitializer.cs.

  2. 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 i Initialize metoda jsou definovány jako static.
    • InitializePizzaContext přijímá objekt jako parametr.
    • Pokud nejsou žádné záznamy v žádné ze tří tabulek, PizzaSauce, a Topping objekty jsou vytvořeny.
    • Objekty Pizza (a jejich Sauce a Topping 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 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:

  1. Do složky Data přidejte nový soubor s názvem Extensions.cs.

  2. 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. Objekt PizzaContext se předává jako parametr.

  3. 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.

  4. 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.

Kontrola znalostí

1.

Předpokládejme, že chcete napsat dotaz jen pro čtení. Jak dáte EF Core na vědomí, že sledování změn grafu objektu není nutné?