Oefening: interactie met gegevens

Voltooid

In de vorige oefening hebt u entiteitsklassen en een databasecontext gemaakt. Vervolgens hebt u EF Core-migraties gebruikt om het databaseschema te maken.

In deze oefening voltooit u de PizzaService implementatie. De service gebruikt EF Core om CRUD-bewerkingen uit te voeren op de database.

Codeer de CRUD-bewerkingen

Voltooi de PizzaService implementatie door de volgende stappen in Services\PizzaService.cs uit te voeren:

  1. Breng de volgende wijzigingen aan, zoals wordt weergegeven in het voorbeeld:

    1. Voeg een using ContosoPizza.Data; richtlijn toe.
    2. Voeg een using Microsoft.EntityFrameworkCore; richtlijn toe.
    3. Voeg een veld op klasseniveau toe voor PizzaContext vóór de constructor.
    4. Wijzig de handtekening van de constructormethode om een PizzaContext parameter te accepteren.
    5. Wijzig de code van de constructormethode om de parameter toe te wijzen aan het veld.
    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
        /// ...
    }
    

    De AddSqlite methodeaanroep die u hebt toegevoegd aan Program.cs eerder geregistreerd PizzaContext voor afhankelijkheidsinjectie. Wanneer het PizzaService exemplaar wordt gemaakt, wordt er een PizzaContext in de constructor geïnjecteerd.

  2. Vervang de GetAll-methode door de volgende code:

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

    In de voorgaande code:

    • De Pizzas verzameling bevat alle rijen in de pizzatabel.
    • Met de AsNoTracking extensiemethode wordt EF Core geïnstrueerd om wijzigingen bijhouden uit te schakelen. Omdat deze bewerking alleen-lezen is, AsNoTracking kunt u de prestaties optimaliseren.
    • Alle pizza's worden geretourneerd met ToList.
  3. Vervang de GetById-methode door de volgende code:

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

    In de voorgaande code:

    • De Include extensiemethode gebruikt een lambda-expressie om op te geven dat de Toppings eigenschappen en Sauce navigatie-eigenschappen moeten worden opgenomen in het resultaat met behulp van gretig laden. Zonder deze expressie retourneert null EF Core deze eigenschappen.
    • De SingleOrDefault methode retourneert een pizza die overeenkomt met de lambda-expressie.
      • Als er geen records overeenkomen, null wordt deze geretourneerd.
      • Als er meerdere records overeenkomen, wordt er een uitzondering gegenereerd.
      • De lambda-expressie beschrijft records waarbij de Id eigenschap gelijk is aan de id parameter.
  4. Vervang de Create-methode door de volgende code:

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

    In de voorgaande code:

    • newPizza wordt ervan uitgegaan dat het een geldig object is. EF Core voert geen gegevensvalidatie uit, dus de ASP.NET Core-runtime of -gebruikerscode moet een validatie afhandelen.
    • De Add methode voegt de newPizza entiteit toe aan de EF Core-objectgrafiek.
    • Met SaveChanges de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
  5. Vervang de AddTopping-methode door de volgende code:

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

    In de voorgaande code:

    • Verwijzingen naar bestaande Pizza en Topping objecten worden gemaakt met behulp van Find.
    • Het Topping object wordt met de .Add methode aan de Pizza.Toppings verzameling toegevoegd. Er wordt een nieuwe verzameling gemaakt als deze niet bestaat.
    • Met SaveChanges de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
  6. Vervang de UpdateSauce-methode door de volgende code:

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

    In de voorgaande code:

    • Verwijzingen naar bestaande Pizza en Sauce objecten worden gemaakt met behulp van Find. Find is een geoptimaliseerde methode voor het opvragen van records op basis van hun primaire sleutel. Find doorzoekt eerst de grafiek van de lokale entiteit voordat er een query op de database wordt uitgevoerd.
    • De Pizza.Sauce eigenschap is ingesteld op het Sauce object.
    • Een Update methodeaanroep is overbodig omdat EF Core detecteert dat u de Sauce eigenschap instelt op Pizza.
    • Met SaveChanges de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
  7. Vervang de DeleteById-methode door de volgende code:

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

    In de voorgaande code:

    • Met Find de methode wordt een pizza opgehaald op basis van de primaire sleutel (in Id dit geval).
    • De Remove methode verwijdert de entiteit in de pizzaToDelete objectgrafiek van EF Core.
    • Met SaveChanges de methode wordt EF Core geïnstrueerd om de objectwijzigingen in de database te behouden.
  8. Sla al uw wijzigingen op en voer deze uit dotnet build. Los eventuele fouten op die optreden.

De database seeden

U hebt de CRUD-bewerkingen gecodeerd voor PizzaService, maar het is eenvoudiger om de leesbewerking te testen als de database goede gegevens bevat. U besluit de app te wijzigen om de database te seeden bij het opstarten.

Waarschuwing

Deze seeding-code voor databases houdt geen rekening met racevoorwaarden, dus wees voorzichtig bij het gebruik ervan in een gedistribueerde omgeving zonder wijzigingen te beperken.

  1. Voeg in de map Gegevens een nieuw bestand toe met de naam DbInitializer.cs.

  2. Voeg de volgende code toe aan 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();
            }
        }
    }
    

    In de voorgaande code:

    • De DbInitializer klasse en Initialize methode zijn beide gedefinieerd als static.
    • Initialize accepteert een PizzaContext object als parameter.
    • Als er geen records in een van de drie tabellen, Pizzaen SauceTopping objecten worden gemaakt.
    • De Pizza objecten (en de bijbehorende Sauce en Topping navigatie-eigenschappen) worden aan de objectgrafiek toegevoegd met behulp van AddRange.
    • De wijzigingen in de objectgrafiek worden doorgevoerd in de database met behulp van SaveChanges.

De DbInitializer klasse is klaar om de database te seeden, maar moet worden aangeroepen vanuit Program.cs. Met de volgende stappen maakt u een extensiemethode voor IHost die aanroepen DbInitializer.Initialize:

  1. Voeg in de map Gegevens een nieuw bestand toe met de naam Extensions.cs.

  2. Voeg de volgende code toe aan 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);
                }
            }
        }
    }
    

    In de voorgaande code:

    • De CreateDbIfNotExists methode wordt gedefinieerd als een uitbreiding van IHost.

    • Er wordt een verwijzing naar de PizzaContext service gemaakt.

    • EnsureCreated zorgt ervoor dat de database bestaat.

      Belangrijk

      Als er geen database bestaat, EnsureCreated maakt u een nieuwe database. De nieuwe database is niet geconfigureerd voor migraties, dus gebruik deze methode met voorzichtigheid.

    • De DbIntializer.Initialize methode wordt aangeroepen. Het PizzaContext object wordt doorgegeven als een parameter.

  3. Vervang ten slotte in Program.cs de // Add the CreateDbIfNotExists method call opmerking door de volgende code om de nieuwe extensiemethode aan te roepen:

    app.CreateDbIfNotExists();
    

    Met deze code wordt de extensiemethode aangeroepen die u eerder hebt gedefinieerd telkens wanneer de app wordt uitgevoerd.

  4. Sla al uw wijzigingen op en voer deze uit dotnet build.

U hebt alle code geschreven die u nodig hebt om eenvoudige CRUD-bewerkingen uit te voeren en de database te seeden bij het opstarten. In de volgende oefening test u deze bewerkingen in de app.

Kennis testen

1.

Stel, u wilt een query schrijven met het kenmerk Alleen-lezen. Hoe laat u EF Core weten dat het niet nodig is om wijzigingen voor de objectgrafiek bij te houden?