Exercício - Interaja com dados
No exercício anterior, você criou classes de entidade e um contexto de banco de dados. Em seguida, você usou migrações do EF Core para criar o esquema de banco de dados.
Neste exercício, você conclui a PizzaService
implementação. O serviço usa o EF Core para executar operações CRUD no banco de dados.
Codifique as operações CRUD
Para concluir a PizzaService
implementação, conclua as seguintes etapas em Serviços\PizzaService.cs:
Faça as seguintes alterações, conforme mostrado no exemplo:
- Adicione uma
using ContosoPizza.Data;
diretiva. - Adicione uma
using Microsoft.EntityFrameworkCore;
diretiva. - Adicione um campo de nível de classe para
PizzaContext
antes do construtor. - Altere a assinatura do método do construtor para aceitar um
PizzaContext
parâmetro. - Altere o código do método do construtor para atribuir o parâmetro ao campo.
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 /// ... }
A
AddSqlite
chamada de método que você adicionou a Program.cs registradaPizzaContext
anteriormente para injeção de dependência. Quando aPizzaService
instância é criada, umPizzaContext
é injetado no construtor.- Adicione uma
Substitua o método
GetAll
pelo código abaixo:public IEnumerable<Pizza> GetAll() { return _context.Pizzas .AsNoTracking() .ToList(); }
No código anterior:
- A
Pizzas
coleção contém todas as linhas na tabela de pizzas. - O
AsNoTracking
método de extensão instrui o EF Core a desativar o controle de alterações. Como essa operação é somente leitura,AsNoTracking
pode otimizar o desempenho. - Todas as pizzas são devolvidas com
ToList
.
- A
Substitua o método
GetById
pelo código abaixo:public Pizza? GetById(int id) { return _context.Pizzas .Include(p => p.Toppings) .Include(p => p.Sauce) .AsNoTracking() .SingleOrDefault(p => p.Id == id); }
No código anterior:
- O
Include
método extension usa uma expressão lambda para especificar que asToppings
propriedades eSauce
navigation devem ser incluídas no resultado usando carregamento ansioso. Sem essa expressão, o EF Core retornanull
para essas propriedades. - O
SingleOrDefault
método retorna uma pizza que corresponde à expressão lambda.- Se nenhum registro corresponder,
null
será retornado. - Se vários registros corresponderem, uma exceção será lançada.
- A expressão lambda descreve registros em que a
Id
propriedade é igual aoid
parâmetro.
- Se nenhum registro corresponder,
- O
Substitua o método
Create
pelo código abaixo:public Pizza Create(Pizza newPizza) { _context.Pizzas.Add(newPizza); _context.SaveChanges(); return newPizza; }
No código anterior:
newPizza
é assumido como um objeto válido. O EF Core não faz validação de dados, portanto, o tempo de execução ou o código do usuário do ASP.NET Core deve lidar com qualquer validação.- O
Add
método adiciona anewPizza
entidade ao gráfico de objeto EF Core. - O
SaveChanges
método instrui o EF Core a persistir as alterações de objeto no banco de dados.
Substitua o método
AddTopping
pelo código abaixo: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(); }
No código anterior:
- As referências a objetos e existentes
Pizza
Topping
são criadas usandoFind
o . - O
Topping
objeto é adicionado àPizza.Toppings
coleção com o.Add
método. Uma nova coleção é criada se ela não existir. - O
SaveChanges
método instrui o EF Core a persistir as alterações de objeto no banco de dados.
- As referências a objetos e existentes
Substitua o método
UpdateSauce
pelo código abaixo: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(); }
No código anterior:
- As referências a objetos e existentes
Pizza
Sauce
são criadas usandoFind
o .Find
é um método otimizado para consultar registros por sua chave primária.Find
pesquisa primeiro o gráfico de entidade local antes de consultar o banco de dados. - A
Pizza.Sauce
propriedade é definida como oSauce
objeto. - Uma
Update
chamada de método é desnecessária porque o EF Core deteta que você definiu aSauce
propriedade emPizza
. - O
SaveChanges
método instrui o EF Core a persistir as alterações de objeto no banco de dados.
- As referências a objetos e existentes
Substitua o método
DeleteById
pelo código abaixo:public void DeleteById(int id) { var pizzaToDelete = _context.Pizzas.Find(id); if (pizzaToDelete is not null) { _context.Pizzas.Remove(pizzaToDelete); _context.SaveChanges(); } }
No código anterior:
- O
Find
método recupera uma pizza pela chave primária (que éId
neste caso). - O
Remove
método remove apizzaToDelete
entidade no gráfico de objetos do EF Core. - O
SaveChanges
método instrui o EF Core a persistir as alterações de objeto no banco de dados.
- O
Salve todas as suas alterações e execute
dotnet build
. Corrija quaisquer erros que ocorram.
Semear o banco de dados
Você codificou as operações CRUD para PizzaService
, mas é mais fácil testar a operação Read se o banco de dados contiver bons dados. Você decide modificar o aplicativo para semear o banco de dados na inicialização.
Aviso
Esse código de propagação de banco de dados não leva em conta as condições de corrida, portanto, tenha cuidado ao usá-lo em um ambiente distribuído sem atenuar as alterações.
Na pasta Dados, adicione um novo arquivo chamado DbInitializer.cs.
Adicione o seguinte código a 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(); } } }
No código anterior:
- A classe e
Initialize
oDbInitializer
método são definidos comostatic
. Initialize
aceita umPizzaContext
objeto como parâmetro.- Se não houver registros em nenhuma das três tabelas,
Pizza
,Sauce
eTopping
objetos serão criados. - Os
Pizza
objetos (e suasSauce
propriedades deTopping
navegação) são adicionados ao gráfico de objetos usandoAddRange
. - As alterações no gráfico de objetos são confirmadas no banco de dados usando
SaveChanges
.
- A classe e
A DbInitializer
classe está pronta para semear o banco de dados, mas precisa ser chamada a partir de Program.cs. As etapas a seguir criam um método de extensão para IHost
que chama DbInitializer.Initialize
:
Na pasta Dados, adicione um novo arquivo chamado Extensions.cs.
Adicione o seguinte código a 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); } } } }
No código anterior:
O
CreateDbIfNotExists
método é definido como uma extensão deIHost
.É criada uma referência ao
PizzaContext
serviço.EnsureCreated garante que o banco de dados exista.
Importante
Se um banco de dados não existir,
EnsureCreated
criará um novo banco de dados. O novo banco de dados não está configurado para migrações, portanto, use esse método com cuidado.O
DbIntializer.Initialize
método é chamado. OPizzaContext
objeto é passado como um parâmetro.
Finalmente, no Program.cs, substitua o
// Add the CreateDbIfNotExists method call
comentário com o seguinte código para chamar o novo método de extensão:app.CreateDbIfNotExists();
Esse código chama o método de extensão que você definiu anteriormente cada vez que o aplicativo é executado.
Salve todas as suas alterações e execute
dotnet build
.
Você escreveu todo o código necessário para fazer operações CRUD básicas e semear o banco de dados na inicialização. No próximo exercício, você testa essas operações no aplicativo.