Övning – Interagera med data

Slutförd

I föregående övning skapade du entitetsklasser och en databaskontext. Sedan använde du EF Core-migreringar för att skapa databasschemat.

I den här övningen slutför du implementeringen PizzaService . Tjänsten använder EF Core för att utföra CRUD-åtgärder i databasen.

Koda CRUD-åtgärderna

Slutför implementeringen PizzaService genom att utföra följande steg i Services\PizzaService.cs:

  1. Gör följande ändringar som du ser i exemplet:

    1. Lägg till ett using ContosoPizza.Data; direktiv.
    2. Lägg till ett using Microsoft.EntityFrameworkCore; direktiv.
    3. Lägg till ett fält på klassnivå för PizzaContext före konstruktorn.
    4. Ändra konstruktormetodens signatur för att acceptera en PizzaContext parameter.
    5. Ändra konstruktormetodkoden för att tilldela parametern till fältet.
    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
        /// ...
    }
    

    Metodanropet AddSqlite som du lade till i Program.cs som tidigare registrerats PizzaContext för beroendeinmatning. När instansen PizzaService skapas matas en PizzaContext in i konstruktorn.

  2. Ersätt metoden GetAll med följande kod:

    public IEnumerable<Pizza> GetAll()
    {
        return _context.Pizzas
            .AsNoTracking()
            .ToList();
    }
    

    I koden ovan:

    • Samlingen Pizzas innehåller alla rader i pizzatabellen.
    • Tilläggsmetoden AsNoTracking instruerar EF Core att inaktivera ändringsspårning. Eftersom den här åtgärden är skrivskyddad AsNoTracking kan du optimera prestanda.
    • Alla pizzor returneras med ToList.
  3. Ersätt metoden GetById med följande kod:

    public Pizza? GetById(int id)
    {
        return _context.Pizzas
            .Include(p => p.Toppings)
            .Include(p => p.Sauce)
            .AsNoTracking()
            .SingleOrDefault(p => p.Id == id);
    }
    

    I koden ovan:

    • Tilläggsmetoden Include tar ett lambda-uttryck för att ange att navigeringsegenskaperna Toppings och Sauce ska inkluderas i resultatet med hjälp av ivrig inläsning. Utan det här uttrycket returnerar EF Core null för dessa egenskaper.
    • Metoden SingleOrDefault returnerar en pizza som matchar lambda-uttrycket.
      • Om inga poster matchar null returneras.
      • Om flera poster matchar utlöses ett undantag.
      • Lambda-uttrycket beskriver poster där Id egenskapen är lika med parametern id .
  4. Ersätt metoden Create med följande kod:

    public Pizza Create(Pizza newPizza)
    {
        _context.Pizzas.Add(newPizza);
        _context.SaveChanges();
    
        return newPizza;
    }
    

    I koden ovan:

    • newPizza antas vara ett giltigt objekt. EF Core utför inte dataverifiering, så validering måste hanteras av ASP.NET Core körnings- eller användarkod.
    • Metoden Add lägger till entiteten newPizza i EF Core-objektdiagrammet.
    • Metoden SaveChanges instruerar EF Core att spara objektändringarna i databasen.
  5. Ersätt metoden UpdateSauce med följande kod:

    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();
    }
    

    I koden ovan:

    • Referenser till befintliga Pizza objekt och Sauce objekt skapas med hjälp Findav . Find är en optimerad metod för att köra frågor mot poster efter deras primärnyckel. Find söker i diagrammet för den lokala entiteten först innan den frågar databasen.
    • Egenskapen Pizza.Sauce är inställd på objektet Sauce .
    • Ett Update metodanrop är onödigt eftersom EF Core identifierar att du anger Sauce egenskapen på Pizza.
    • Metoden SaveChanges instruerar EF Core att spara objektändringarna i databasen.
  6. Ersätt metoden AddTopping med följande kod:

    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();
    }
    

    I koden ovan:

    • Referenser till befintliga Pizza objekt och Topping objekt skapas med hjälp Findav .
    • Objektet Topping läggs till i Pizza.Toppings samlingen med .Add metoden . En ny samling skapas om den inte finns.
    • Metoden SaveChanges instruerar EF Core att spara objektändringarna i databasen.
  7. Ersätt metoden DeleteById med följande kod:

    public void DeleteById(int id)
    {
        var pizzaToDelete = _context.Pizzas.Find(id);
        if (pizzaToDelete is not null)
        {
            _context.Pizzas.Remove(pizzaToDelete);
            _context.SaveChanges();
        }        
    }
    

    I koden ovan:

    • Metoden Find hämtar en pizza med primärnyckeln (vilket är Id i det här fallet).
    • Metoden Remove tar bort entiteten pizzaToDelete i EF Cores objektdiagram.
    • Metoden SaveChanges instruerar EF Core att spara objektändringarna i databasen.
  8. Spara alla ändringar och kör dotnet build. Åtgärda eventuella fel som inträffar.

Seed databasen

Du har kodat CRUD-åtgärderna för PizzaService, men det är enklare att testa läsåtgärden om databasen innehåller bra data. Du bestämmer dig för att ändra appen så att den får databasen vid start.

Varning

Den här databassåddkoden tar inte hänsyn till konkurrensförhållanden, så var försiktig när du använder den i en distribuerad miljö utan att minimera ändringarna.

  1. Lägg till en ny fil med namnet DbInitializer.cs i mappen Data.

  2. Lägg till följande kod i 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();
            }
        }
    }
    

    I koden ovan:

    • Klassen DbInitializer och Initialize metoden definieras båda som static.
    • Initialize accepterar ett PizzaContext objekt som en parameter.
    • Om det inte finns några poster i någon av de tre tabellerna Pizzaskapas , Sauceoch Topping objekt.
    • Objekten Pizza (och deras Sauce och Topping navigeringsegenskaperna) läggs till i objektdiagrammet med hjälp AddRangeav .
    • Objektdiagramändringarna checkas in i databasen med hjälp SaveChangesav .

Klassen DbInitializer är redo att skicka databasen, men den måste anropas från Program.cs. Följande steg skapar en tilläggsmetod för IHost som anropar DbInitializer.Initialize:

  1. Lägg till en ny fil med namnet Extensions.cs i mappen Data.

  2. Lägg till följande kod i 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);
                }
            }
        }
    }
    

    I koden ovan:

    • Metoden CreateDbIfNotExists definieras som ett tillägg för IHost.

    • En referens till PizzaContext tjänsten skapas.

    • Se till attSkapa säkerställer att databasen finns.

      Viktigt

      Om det inte finns EnsureCreated någon databas skapar du en ny databas. Den nya databasen har inte konfigurerats för migreringar, så använd den här metoden med försiktighet.

    • Metoden DbIntializer.Initialize anropas. Objektet PizzaContext skickas som en parameter.

  3. I Program.cs ersätter du slutligen kommentaren // Add the CreateDbIfNotExists method call med följande kod för att anropa den nya tilläggsmetoden:

    app.CreateDbIfNotExists();
    

    Den här koden anropar tilläggsmetoden som du definierade tidigare varje gång appen körs.

  4. Spara alla ändringar och kör dotnet build.

Du har skrivit all kod du behöver för att utföra grundläggande CRUD-åtgärder och dirigera databasen vid start. I nästa övning testar du dessa åtgärder i appen.

Kontrollera dina kunskaper

1.

Anta att du vill skriva en skrivskyddad fråga. Hur indikerar du för EF Core att spårning av ändringar i objektdiagram är onödigt?