/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - e como usar Stored Procedures? /// - motivação: chamar a SP que retorna o total de atores distintos que atuaram em filmes de uma categoria qualquer /// - usar uma SP que passa parâmetros /// - método DbContext.Database.ExecuteSqlCommand /// - importância de definir corretamente os parâmetros /// - new SqlParameter /// - usar parâmetros de saída /// - mostrar no console /// - dizer que é possível também enviar comandos UPDATE, INSERT e DELETE /// - e que o método retornar os registros afetados /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { //habilitar o log depois de executar uma vez o LINQ! contexto.StartLogSqlToConsole(); var categ = "Games"; var nomeCateg = new SqlParameter { ParameterName = "category_name", Size = 25, Value = categ }; var totalAtores = new SqlParameter { ParameterName = "total_actors", Size = 4, Direction = System.Data.ParameterDirection.Output }; contexto.Database .ExecuteSqlCommand( "total_actors_from_given_category", nomeCateg, totalAtores); Console.WriteLine($"O total de atores distintos da categoria {categ} é {totalAtores.Value}."); } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - também é possível enviar comandos UPDATE, INSERT e DELETE /// - por exemplo, queremos dar carga em alguns registros /// - migration? /// - e que o método retornar os registros afetados /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { //habilitar o log depois de executar uma vez o LINQ! contexto.StartLogSqlToConsole(); //var insertSql = // @"INSERT INTO language (name) VALUES // ('Portuguese'), ('Spanish'), ('Zulu');"; //var registrosAfetados = contexto // .Database // .ExecuteSqlCommand(insertSql); //Console.WriteLine($"Foram afetados {registrosAfetados} registros na execução do último comando."); var updateSql = @"UPDATE film SET release_year = 1994 WHERE film_id > 750 AND film_id < 830"; var registrosAfetados = contexto .Database .ExecuteSqlCommand(updateSql); Console.WriteLine($"Foram afetados {registrosAfetados} registros na execução do último comando."); var filmesDe94 = contexto.Filmes.Where(f => f.AnoLancamento == "1994"); Console.WriteLine($"Filmes de 94 são {filmesDe94.Count()}"); } }
/// <summary> /// Pré-requisitos: /// - bando de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - agora vamos relacionar atores a filmes /// - sabemos que um ator pode estrelar vários filmes /// - ...e um filme tem um elenco com vários atores /// - relacionamento Muitos para Muitos /// - no curso anterior vimos que o EF Core não suporta M para N sem uma classe de join /// - vamos modelar isso em nossas classes /// - criar classe FilmeAtor, com propriedades de navegação Filme e Ator /// - e a chave primária? No banco ela é formada pela junção de actor_id e film_id /// - criar classe de configuração com a definição da chave /// - gerar o script e verificar se está ok /// - observar que essa tabela não serve de nada, precisamos dos relacionamentos /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); var info = contexto.GetDBTableInfo(typeof(FilmeAtor)); Console.WriteLine(info.NomeTabela); } }
/// <summary> /// Pré-requisitos: /// - bando de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - configurar os nomes de tabelas e colunas usando Fluent API no método OnModelCreating /// - executar e observar que o programa vai funcionar exatamente como antes! /// - explicar que no processo de mapeamento, o terceiro passo é Verificar o método OnModelCreating /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); foreach (var ator in contexto.Atores) { System.Console.WriteLine(ator); } } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - vamos mapear agora as tabelas staff e customer /// - representam Funcionarios /// - e Clientes /// - vamos criar a classe Funcionario /// - e configurar seu mapeamento /// - atenção para os tipos de dados /// - fazer um SELECT pra testar; show! /// - agora vamos criar a classe Cliente /// - peraí! as propriedades são muito parecidas!! /// - poderiam fazer parte de uma herança /// - como mapear no EF? /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); foreach (var func in contexto.Funcionarios) { Console.WriteLine(func); } } }
/// <summary> /// Pré-requisitos: /// - bando de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - observar os valores do campo Rating. /// - no banco existe uma restrição CHECK que valida os seguintes valores: /// ADD CONSTRAINT [CHECK_special_rating] CHECK ([rating]='NC-17' OR [rating]='R' OR [rating]='PG-13' OR [rating]='PG' OR [rating]='G'); /// que significam (G) - Livre, (PG) - Menores10, (PG-13) - Menores13, (R) - Restrito, (NC-17)- Maiores18 /// - como podemos mapear no lado das classes? Enums! /// - e qual a convenção para Enumerados no Entity? /// - olhar o script para descobrir que vai mapear para um int /// - criamos o enum, e criamos uma prop desse tipo /// - mas dps q fazemos a mudança pra enum, dá erro de conversão! /// - como resolver? /// - duas propriedades: uma do tipo string com private set /// - e outra do tipo enum, mas com getter e setter explícito para converter a string em enum e vice versa /// - além disso, informar ao EF que queremos ignorar a prop enum /// - testar o SELECT novamente /// - /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); foreach (var filme in contexto.Filmes) { System.Console.WriteLine(filme); } //var f = new Filme(); //f.ClassificacaoIndicativa = ClassificacaoIndicativa.Livre; //System.Console.WriteLine(f.Classificacao); } }
/// <summary> /// Pré-requisitos: /// - bando de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - mostrar que a coluna last_update tem um valor padrão para data/hora atual quando inserida /// - maiores detalhes em https://docs.microsoft.com/en-us/ef/core/modeling/relational/default-values /// - explicar as convenções /// - como mapear essa estratégia para a coluna last_update? /// - só pode ser definida via Fluent API /// - através do método HasDefaultValueSql("getdate()") /// /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); var atorQualquer = contexto.Atores.First(); MostraDataAtualizacaoAtor(contexto, atorQualquer); atorQualquer.UltimoNome = "STALLONE"; contexto.SaveChanges(); MostraDataAtualizacaoAtor(contexto, atorQualquer); } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - como superar as limitações do EF? /// - fazer um SELECT com join entre a view e a tabela da entidade /// - dá pra usar LINQ depois do FromSql, por exemplo Include /// - e daí conseguimos pegar o total de filmes /// - usando o nosso SELECT ideal! /// - mas esse SELECT não é bom ficar chapado na string /// - podemos usar uma VIEW armazenada no banco de dados /// - mas como usar no EF? É possível? /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { //habilitar o log depois de executar uma vez o LINQ! contexto.StartLogSqlToConsole(); //recuperando via LINQ //var atoresMaisAtuantesPorLinq = contexto.Atores // .Include(a => a.Filmografia) // .Select(a => new { PrimeiroNome = a.PrimeiroNome, TotalFilmes = a.Filmografia.Count }) // .ToList() // .OrderByDescending(a => a.TotalFilmes) // .Take(5); //atoresMaisAtuantesPorLinq // .ToList() // .ForEach(a => MostrarAtuacao(a.PrimeiroNome, a.TotalFilmes)); //como executamos muitos SELECTs, optamos por chamar uma view... //var atoresMaisAtuantesPorSP = contexto.Atores // .FromSql("select * from dbo.top5_most_starred_actors"); //foreach (var ator in atoresMaisAtuantesPorSP) //{ // Console.WriteLine(ator); //} //mas o EF tem limitações quanto a chamar Views e SPs (ver acima) //então lidamos com essas limitações para retornar apenas os atores var selectSQL = @"select a.* from actor a inner join ( select top 5 a.actor_id, count(fa.film_id) as total from actor a inner join film_actor fa on fa.actor_id = a.actor_id group by a.actor_id, a.first_name, a.last_name order by total ) top5 on a.actor_id = top5.actor_id"; var atoresMaisAtuantes = contexto.Atores .FromSql(selectSQL) .Include(a => a.Filmografia); atoresMaisAtuantes .ToList() .ForEach(a => MostrarAtuacao(a.PrimeiroNome, a.Filmografia.Count)); } }
/// <summary> /// Atualiza 20 filmes com idiomas originais randômicos. /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); var filmes = contexto.Filmes.Take(20); var idiomas = contexto.Idiomas.ToList(); foreach (var filme in filmes) { filme.IdiomaOriginal = idiomas.RandomItem(); } contexto.SaveChanges(); } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - mas esse SELECT não é bom ficar chapado na string /// - podemos usar uma VIEW armazenada no banco de dados /// - mas como usar no EF? É possível? /// - mostrar docs da Microsoft dizendo que não é suportado /// - que existe uma issue para implementar no futuro /// - mas é possível usar do mesmo jeito que fizemos no vídeo anterior /// - legal! /// - e com Stored Procedures? /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { //habilitar o log depois de executar uma vez o LINQ! contexto.StartLogSqlToConsole(); //para usar views fazemos igualzinho ao vídeo anterior! var atoresMaisAtuantes = contexto.Atores .FromSql("select a.* from actor a inner join dbo.top5_most_starred_actors top5 on a.actor_id = top5.actor_id") .Include(a => a.Filmografia); atoresMaisAtuantes .ToList() .ForEach(a => MostrarAtuacao(a.PrimeiroNome, a.Filmografia.Count)); } }
/// <summary> /// Pré-requisitos: /// - bando de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - observar que essa tabela não serve de nada, precisamos dos relacionamentos /// - queremos fazer contexto.Filmes.Include(f => f.Elenco).ThenInclude(e => e.Ator); /// - introduzir os termos do EF para relações (PPT!): /// - entidade Dependente (ex. FilmeAtor) /// - entidade Principal (ex. Filme/Ator) /// - Chave Estrangeira (ex. FilmeAtor.FilmeId/FilmeAtor.AtorId) /// - Chave Principal (ex. Filme.Id/Ator.Id) /// - Propriedade de Navegação: /// - Propriedade de Navegação do tipo Coleção (ex. Filme.Elenco/Ator.Filmografia) /// - Propriedade de Navegação do tipo Referência (ex. FilmeAtor.Filme/FilmeAtor.Ator) /// - Propriedade de Navegação Inversa (ex. Filme.Elenco = FilmeAtor.Filme / Ator.Filmografia = FilmeAtor.Ator) /// - criar as propriedades de navegação de coleção nas entidades principais Filme e Ator /// - testar o SELECT, dará o sgte erro: /// Exceção Sem Tratamento: System.Data.SqlClient.SqlException: /// Invalid column name 'AtorId'. /// Invalid column name 'FilmeId'. /// - mostrar as convenções do EF, e observar que do jeito que fizemos /// o EF está procurando pelas colunas FilmeId e AtorId /// - repare no SELECT gerado pelo join /// SELECT [f0].[film_id], [f0].[actor_id], [f0].[AtorId], [f0].[FilmeId], [a].[actor_id], [a].[first_name], [a].[last_name], [a].[last_update] /// FROM[film_actor] AS[f0] /// INNER JOIN( /// SELECT DISTINCT TOP(1) [f].[film_id] /// FROM[film] AS[f] /// ORDER BY[f].[film_id] /// ) AS[f1] ON[f0].[FilmeId] = [f1].[film_id] /// LEFT JOIN[actor] AS[a] ON[f0].[AtorId] = [a].[actor_id] /// ORDER BY[f1].[film_id] /// - configurar as chaves estrangeiras: /// - entityBuilder.HasOne(fa => fa.Filme).WithMany(f => f.Elenco).HasForeignKey("film_id"); /// - entityBuilder.HasOne(fa => fa.Ator).WithMany(a => a.Filmografia).HasForeignKey("actor_id"); /// - testar o SELECT; vai funfar! /// - testar o script DDL /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); var filme = contexto.Filmes .Include(f => f.Elenco) .ThenInclude(e => e.Ator) .First(); Console.WriteLine($"Mostrando o elenco de {filme.Titulo}:"); foreach (var item in filme.Elenco) { Console.WriteLine(item.Ator); } } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - como mapear herança no EF? /// - convenções de herança: TPH /// - EF Core não implementa TPC, mas não precisamos pq vamos colocar no contexto apenas Funcionario e Cliente /// - criar a classe abstrata pai: Pessoa /// - propriedades Id, PrimeiroNome, UltimoNome, Email e Ativo /// - herdar Funcionario de Pessoa /// - testar o SELECT; deve funfar /// - criar a classe Cliente /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); Console.WriteLine("Listando os funcionários.."); foreach (var func in contexto.Funcionarios) { Console.WriteLine(func); } Console.WriteLine("Listando os clientes..."); foreach (var cliente in contexto.Clientes.Take(10)) { Console.WriteLine(cliente); } } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - motivação para fazer SQL puro: /// - performance /// - a query não pode ser expressa via LINQ /// - insatisfação com o modo como EF gerou o SQL /// - motivação: pegar os 5 atores que mais atuaram em filmes /// - fazer via LINQ >> funciona! /// - porém, mostrar o SQL com StartLog... /// - não satisfatório /// - mostrar o SELECT ideal numa janela de consulta SQL Server /// - como faço para usar esse SELECT ideal? /// - usar o FromSQL /// - executando direto sem preocupar-se com as limitações, erro: /// System.InvalidOperationException: The required column 'last_update' was not present in the results of a 'FromSql' operation. /// - será que não conseguimos usar views ou stored procedures? /// - mostrar as limitações no documento da MS /// - limitações que devem ser consideradas quando usar SQL puro: /// - as queries só podem retornar tipos que fazem parte do modelo /// - as queries devem obrigatoriamente retornar dados para TODAS as propriedades /// - nomes das colunas na query deve ser iguais as colunas mapeadas pelas propriedades /// - a query não pode conter dados relacionados /// - aplicar as limitações /// - /// </summary> public static void Main() { using (var contexto = new AluraFilmesContexto()) { //habilitar o log depois de executar uma vez o LINQ! contexto.StartLogSqlToConsole(); //recuperando via LINQ var atoresMaisAtuantesPorLinq = contexto.Atores .Include(a => a.Filmografia) .Select(a => new { PrimeiroNome = a.PrimeiroNome, TotalFilmes = a.Filmografia.Count }) .ToList() .OrderByDescending(a => a.TotalFilmes) .Take(5); atoresMaisAtuantesPorLinq .ToList() .ForEach(a => MostrarAtuacao(a.PrimeiroNome, a.TotalFilmes)); } }
static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); var frances = contexto.Idiomas.Where(i => i.Nome == "French").First(); if (frances != null) { var primeirosDezFilmes = contexto.Filmes.Take(10).ToList(); primeirosDezFilmes.ForEach(f => f.IdiomaFalado = frances); contexto.SaveChanges(); } else { Console.WriteLine("Não tinha um idioma com esse nome..."); foreach (var idioma in contexto.Idiomas) { Console.WriteLine(idioma); } } } }
/// <summary> /// Pré-requisitos: /// - banco de dados AluraFilmes criado e populado /// - EF Core instalado no projeto /// - classe LogSQLExtensions criada para logar o SQL /// Objetivos: /// - vamos mapear agora a relação Filme x Idioma /// - repare que um filme tem dois idiomas: um original (que pode ser nulo), outro que indica o idioma da mídia em estoque (não nulo) /// - neste caso, como fazer? neste caso não podemos contar com as convenções /// - primeiro vamos criar a classe Idioma e configurar seu mapeamento /// - fazer um SELECT pra testar /// - erro no tipo language_id: mudar prop pra byte /// - funfou /// - agora mapear os 2 relacionamentos 1 x N - TESTAR SE VAI FUNFAR SEM CONFIGURAR /// - 1o) relação IdiomaFalado; relembrar termos... /// - Entidade Principal: Idioma /// - Entidade Dependente: Filme /// - Chave Estrangeira: shadow property Filme.language_id /// - Chave Principal: Idioma.Id /// - Navegação Coleção: Idioma.FilmesFalados /// - Navegação Referência: Filme.IdiomaFalado /// - criar relação /// - lembrar que essa relação é NOT NULL (script tem que acusar...) /// - 2o) relação IdiomaOriginal; relembrar termos: /// - Entidade Principal: Idioma /// - Entidade Dependente: Filme /// - Chave Estrangeira: shadow property Filme.original_language_id /// - Chave Principal: Idioma.Id /// - Navegação Coleção: Idioma.FilmesOriginais /// - Navegação Referência: Filme.IdiomaOriginal /// - criar relação /// - lembrar que essa relação pode ser NULL (script tem que acusar...) /// - ou seja, colocar Property<byte?> na shadow property /// - fazer um SELECT em filmes com idioma falado == "French" /// - necessário rodar um script de carga /// </summary> static void Main() { using (var contexto = new AluraFilmesContexto()) { contexto.StartLogSqlToConsole(); foreach (var idioma in contexto.Idiomas) { Console.WriteLine(idioma); } var idiomaPesquisado = "French"; Console.WriteLine($"\nFilmes cujo idioma falado é {idiomaPesquisado}:"); var filmesNoIdiomaPesquisado = contexto.Filmes .Include(f => f.IdiomaFalado) .Include(f => f.IdiomaOriginal) .Where(f => f.IdiomaFalado.Nome == idiomaPesquisado); foreach (var filme in filmesNoIdiomaPesquisado) { Console.WriteLine(filme); } } }