###### tags: `Entity Framework Core` # Dominando o Entity Framework Core ## Forçando um migração * Não e uma boa pratica ```csharp= static void AplicarMigracaoEmTempoExecucao(){ using var db = new Curso.Data.ApplicationContext(); db.DataBase.Migrate(); } ``` Comando: dotnet ef database drop --context applicationContext [Excluir o banco de dados] dotnet run ## EF Database ### Recuperando migrações existente em sua aplicação ```csharp= static void TodasMigracoes(){ using var db = new Curso.Data.ApplicationContext(); var migracoes = db.DataBase.GetMigrations(); Console.WriteLine($"Total: {migracoes.Count()}"); foreach(var migracao in migracoes){ Console.WriteLine($"Migração: {migracao}"); } } ``` ### Recuperando migrações aplicadas em seu banco de dados ```csharp= dotnet ef migrations list --context ApplicationContext ``` ```csharp= static void MigracoesJaAplicadas(){ using var db = new Curso.Data.ApplicationContext(); var migracoes = db.DataBase.GetAppliedMigrations(); Console.WriteLine($"Total: {migracoes.Count()}"); foreach(var migracao in migracoes){ Console.WriteLine($"Migração: {migracao}"); } } ``` ### Gerando o script de criação SQL do modelo de dados ```csharp= static void ScriptGeralDoBancoDeDados(){ using var db = new Curso.Data.ApplicationContext(); var script = db.DataBase.GenerateCreateScript(); Console.WriteLine(script); } ``` ## Tipos de carregamentos - DeepDlve * Adiantado * Explícito * Lento (Famoso LazyLoad) ### Consultando dados usando carregamento adiantado (Eager) ```csharp= static void CarregamentoAdiantado(){ using var db = new Curso.Data.ApplicationContext(); //Popular a base de dados SetupTiposCarregamentos(db); var departamentos = db .Departamentos .Include(p=>p.Funcionarios); foreach(var departamento in departamentos){ Console.WriteLine("--------------------------------------"); Console.WriteLine($"Departamento: {departamento.Descricao}"); if (departamento.Funcionarios?.Any() ?? false){ foreach(var funcionario in departamento.Funcionarios){ Console.WriteLine($"\tFuncionario: {funcionario.nome}"); } } else { Console.WriteLine($"\tNenhum funcionário encontrado"); } } } ``` * Desvantagem deste tipo de abordagem ocorre quando existe muitos campos na tabela de departamento e/ou funcionários desta causando uma sobrecarga no trafico de dados da rede. ### Consultando dados usando carregamento explícito (Explicitly) * Significa que os dados relacionados são explicitamente carregados do banco de dados em um momento posterior, ou seja, quando você solicitar os dados. * MultipleActiveResultSets=true = Habilita o sql para permitir executar multipos lotes de consulta em uma unica conexão. ```csharp= static void CarregamentoExplicito(){ using var db = new Curso.Data.ApplicationContext(); //Popular a base de dados SetupTiposCarregamentos(db); var departamentos = db .Departamentos .ToList(); foreach(var departamento in departamentos){ if (departamento.Id == 2){ //db.Entry(departamento).Collection(p=>p.Funcionarios).Load(); db.Entry(departamento).Collection(p=>p.Funcionarios).Query().Where(p=>p.Id > 2).ToList(); } Console.WriteLine("--------------------------------------"); Console.WriteLine($"Departamento: {departamento.Descricao}"); if (departamento.Funcionarios?.Any() ?? false){ foreach(var funcionario in departamento.Funcionarios){ Console.WriteLine($"\tFuncionario: {funcionario.nome}"); } } else { Console.WriteLine($"\tNenhum funcionário encontrado"); } } } ``` ## Consultas ### Consultando dados usando carregamneto lento (LazyLoad) * Significa que os dados relacionados são carregados sob demanda do banco de dados quando a propriedade de navegação é acessada. ```shell= dotnet add package Microsof.EntityFrameworkCore.Proxies --version 5.0.0 ``` * E necessário habilitar no contexto a propriedade `UseLazyLoadingProxies()` * As propriedades de navegação precisa ter a anotação `virtual` * E necessário ter cuidado ao utilizar este tipo de abordagem pois para cada referencia serializada e executada uma consulta na base de dados. * Ijetando [ILazyLoader] * Ijetando [Action] * Desabilitando o LazyLoadingEnabled para uma determinada consulta. ```csharp= static void CarregamentoLento(){ using var db = new Curso.Data.ApplicationContext(); //Popular a base de dados SetupTiposCarregamentos(db); //db.ChangeTracker.LazyLoadingEnabled = false; var departamentos = db .Departamentos .ToList(); foreach(var departamento in departamentos){ Console.WriteLine("--------------------------------------"); Console.WriteLine($"Departamento: {departamento.Descricao}"); if (departamento.Funcionarios?.Any() ?? false){ foreach(var funcionario in departamento.Funcionarios){ Console.WriteLine($"\tFuncionario: {funcionario.nome}"); } } else { Console.WriteLine($"\tNenhum funcionário encontrado"); } } } ``` ### Configurando um Filtro Global Exemplo de filtro global ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Departamento>().HasQueryFilter(p=>!p.Excluido); } static void FiltroGlobal() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var departamentos = db.Departamentos.Where(p => p.Id > 0).ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao} \t Excluido: {departamento.Excluido}"); } } ``` ### Ignorando um filtro Global ```csharp= static void IgnoreFiltroGlobal() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var departamentos = db.Departamentos.IgnoreQueryFilters().Where(p => p.Id > 0).ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao} \t Excluido: {departamento.Excluido}"); } } ``` ### Consultas Projetadas ```csharp= static void ConsultaProjetada() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var departamentos = db.Departamentos .Where(p => p.Id > 0) .Select(p => new { p.Descricao, Funcionarios = p.Funcionarios.Select(f => f.Nome) }) .ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao}"); foreach (var funcionario in departamento.Funcionarios) { Console.WriteLine($"\t Nome: {funcionario}"); } } } ``` ### Criando consultas parametrizadas ```csharp= static void ConsultaParametrizada() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var id = new SqlParameter { Value = 1, SqlDbType = System.Data.SqlDbType.Int }; var departamentos = db.Departamentos .FromSqlRaw("SELECT * FROM Departamentos WHERE Id>{0}", id) .Where(p => !p.Excluido) .ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao}"); } } ``` ### Criando consultas interpolada() ```csharp= static void ConsultaInterpolada() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var id = new SqlParameter { Value = 1, SqlDbType = System.Data.SqlDbType.Int }; var departamentos = db.Departamentos .FromSqlInterpolated($"SELECT * FROM Departamentos WHERE Id>{id}" ) .ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao}"); } } ``` ### Usando o recuso TAG em suas consultas para auditar comandos * Comentarios em consultas para banco de dados ```csharp= static void ConsultaComTAG() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var departamentos = db.Departamentos .TagWith("Estou enviando um comentário para o servidor") .ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao}"); } } ``` ### Entendendo a diferença em consultas 1xN vs Nx1 ```csharp= static void EntendendoConsulta1NN1() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var Funcionarios = db.Funcionarios .Include(p=>p.Departamento) .ToList(); foreach (var funcionario in funcionarios) { Console.WriteLine($"Nome: {funcionario.nome} / Descrição do departamento: {funcionario.Departamento.Descricao}"); } // var departamentos = db.Departamentos // .Include(p=>p.funcionarios) // .ToList(); // foreach (var departamento in departamentos) // { // Console.WriteLine($"Descrição: {departamento.Descricao}"); // foreach (var funcionario in departamento.Funcionarios){ // Console.WriteLine($"Nome: {funcionario.nome}") // } } } ``` ### Divisão de consultas com SplitQuery A divisão de consultas basicamente veio para solucionar um problema, e basicamente o problema é quando precisamos carregar dados relacionados. Quantidade de informação trafegando na rede * explosão carteziana ```csharp= static void DivisaoDeConsulta() { using var db = new Curso.Data.ApplicationContext(); Setup(db); var departamentos = db.Departamentos .Include(p=>p.Funcionarios) .Where(p=> p.Id < 3) .AsSplitQuery() .ToList(); foreach (var departamento in departamentos) { Console.WriteLine($"Descrição: {departamento.Descricao}"); foreach (var funcionario in departamento.Funcionarios){ Console.WriteLine($"Nome: {funcionario.nome}") } } ``` Configurando a aplicação a nível global No OnConfiguring do DataContext adicione o metodo de extensção em UseSqlServer(strConnection, p=>p.UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)) Ignorando o SplitQuery .AsSingleQuery(); ## Stored Procedures * Criando uma procedure de inserção * Executando uma inserção via procedure * Criando uma procedure de consulta * Executando uma consulta via procedure ### Criando uma procedure de insersão ```csharp= static void CriarStoredProcedure(){ var criarDepartamento = @" CREATE OR ALTER PROCEDURE CriarDepartamento @Descricao VACHAR(50), @Ativo bit AS BEGIN INSERT INTO Departamentos(Descricao, Ativo, Excluido) VALUES (@Descricao, @Ativo, 0) END "; } ``` ### Executando inserção via procedure ```csharp= static void InserirDadosViaProcedure(){ using var db = new Curso.Data.ApplicationContext(); db.Database.ExecuteSqlRaw("Execute CriarDepartamento @p0, @p1", "Departamento via procedure", true); } ``` ### Criando uma procedure de consulta ```csharp= static void CriarStoredProcedureDeConsulta(){ var criarDepartamento = @" CREATE OR ALTER PROCEDURE GetDepartamentos @Descricao VACHAR(50), @Ativo bit AS BEGIN SELECT * FROM Departamentos Where Descricao like @Descricao + '%' END "; } ``` ### Executando uma consulta via procedure ```csharp= static void ConsultaViaProcedure(){ using var db = new Curso.Data.ApplicationContext(); var dep = new SqlParameter("@dep", "Departamento"); var departamentos = db.Departamentos //.FromSqlRaw("EXECUTE GetDepartamentos @p0", "Departamento") //.FromSqlRaw("EXECUTE GetDepartamentos @dep", dep) .FromSqlInterpolated($"EXECUTE GetDepartamentos {dep}") .ToList(); foreach(var departamento in departamentos){ Console.WriteLine(departamentos.Descricao); } } ``` ## Infraestrutura * Configurando log simplificado * Filtrando categoria do log * Excrevendo log em um arquivo * Habilitando erros detalhandos * Habilitando visualização dos dados sensíveis * Configurando o batch size * Configurando o timeout do comando blobal * Configurando o timeout para um comando para um fluxo * Configurando resiliência para sua aplicação * Criando uma estratégia de resiliência ### Configurando um log simplificado ```csharp= public class ApplicationContext : DbContext{ public DbSet<Departamento> Departamentos {get;set;} public DbSet<Funcionario> Funcionarios {get;set;} } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection) //.LogTo(Console.WriteLine); .LogTo(Console.WriteLine, LogLevel.Information); } ``` ```csharp= class Program{ static void Main(string[] args){ } static void ConsultarDepartamentos(){ using var db = new Curso.Data.ApplicationContext(); var departamentos = db.Departamentos.Where(p=>p.Id > 0).ToArray(); } } ``` ### Filtrando eventos de seus logs ```csharp= public class ApplicationContext : DbContext{ public DbSet<Departamento> Departamentos {get;set;} public DbSet<Funcionario> Funcionarios {get;set;} } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection) //.LogTo(Console.WriteLine); //.LogTo(Console.WriteLine, LogLevel.Information); .LogTo(Console.WriteLine, new[] {RelationalEventId.CommandExecuted, CoreEventId.ContextInitialized}, Loglevel.Information, DbContextLoogerOption.LocalTime | DbContextLoggerOptions.SingleLine) } ``` ### Gravando seus logs em um arquivo ```csharp= public class ApplicationContext : DbContext{ private readonly StreamWriter _writer = new StreamWriter("meu_log_ef_core.txt", append: true); public DbSet<Departamento> Departamentos {get;set;} public DbSet<Funcionario> Funcionarios {get;set;} } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection) .LogTo(_write.WriteLine, LogLevel.Information); } public override void Dispose() { base.dispose(); _write.Dispose(); } ``` ### Habilitando erros detalhados * Utilizar esta funcionalidade somente em tempo de debug ```csharp= public class ApplicationContext : DbContext{ private readonly StreamWriter _writer = new StreamWriter("meu_log_ef_core.txt", append: true); public DbSet<Departamento> Departamentos {get;set;} public DbSet<Funcionario> Funcionarios {get;set;} } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection) .EnableDetailedErrors(); } ``` ### Habilitando visualização dos dados sensíveis * E necessário habilitar o logLevel no Context * Habilitar [EnableSensitiveDatalogging] ```csharp= static void DadosSensiveis(){ using var db = new Curso.Data.ApplicationContext(); var dep = new SqlParameter("@dep", "Departamento"); var descricao = "Departamento" var departamentos = db.Departamentos.Where(p=>p.Descricao == descricao).ToArray(); } ``` ### Habilitando Batch Size * Quantidade padrão 42 registro por lote (performance) * limite parametros 2100 parametros * Utilizar apenas quando tem uma rede instavel, para evitar do sistema fazer chamada na base de dados; * Para bancos de dados na nuvem a latencia e maior deve se tomar cuidado, e avaliar a melhor solução; ```csharp= protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection, o=>o.MaxBatchSize(100)) .EnableDetailedErrors(); } static void HabilitandoBatchSize(){ using var db = new Curso.Data.ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); for (var i=0;<50;i++){ db.Departamentos.Add( new Departamento { Descricao="Departamento " + i }); } } ``` ### Configurando o Timeout do comando global ```csharp= protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection, o=>o.MaxBatchSize(100).CommandTimeout(5)) .EnableDetailedErrors(); } static void TempoComandoGeral(){ using var db = new Curso.Data.ApplicationContext(); db.DataBase.ExecuteSqlRaw("WAITFOR DELAY '00:00:07'; SELECT 1"); } ``` ### Configurando o Timeout do comando para um fluxo ```csharp= static void TempoComandoGeral(){ using var db = new Curso.Data.ApplicationContext(); db.DataBase.SetCommandTimeout(); db.DataBase.ExecuteSqlRaw("WAITFOR DELAY '00:00:07'; SELECT 1"); } ``` ### Habilitando resiliência para sua aplicação ```csharp= protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer( strConnection, o=>o .MaxBatchSize(100) .CommandTimeout(5) .EnableRetryOnFailure(4, TimeSpan.FromSecounds(10), null)) .LogTo(Console.WriteLine, LogLevel.Information) .EnableSensitiveDataLogging() ) } ``` ### Criando uma estratégia de resiliência ```csharp= protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer( strConnection, o=>o .MaxBatchSize(100) .CommandTimeout(5) .EnableRetryOnFailure(4, TimeSpan.FromSecounds(10), null)) .LogTo(Console.WriteLine, LogLevel.Information) .EnableSensitiveDataLogging() ) } static void ExecutarEstrategiaResiliencia(){ using var db = new Curso.Data.ApplicationContext(); var strategy = db.Database.CreateExecutionStrategy(); strategy.Execute(() => { using var transaction = db.Database.BeginTransaction(); db.Departamentos.Add(new Departamento {Descricao = "Departamento transação"}); db.SaveChanges(); transaction.Commit(); }) } ``` ## Modelo de dados * Colletions * Sequências * Índices * Propagação de dados * Esquemas * Conversor de valores * Criar um conversor de valor customizado * Propriedades de sombra * Owned Types * Relacionamento 1 x 1 * Relacionamento 1 x N * Relacionamento N X N * Campo de apoio (Backing field) * TPH e TPT * Sacola de proriedades (Property Bags) ### Collations EF Core 5 add Suporte ao Collations recurso oferecido disponível nos principais banco de dados do mercado. ```csharp= protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder){ const string strConnection = @"Data source=(localdb)\\mssqllocaldb;initial Catalog=DevIO-02; Integrated Security=true;pooling=true;" optionBuilder .UseSqlServer(strConnection) .LogTo(Console.WriteLine, LogLevel.Information) .EnableSensitiveDataLogging(); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.UseCollation('SQL_Latin1_General_CP1_CI_AI') //STANLEY => stanley //João => Joao modelBuilder.Entity<Departamento>().Property(p=>p.Descricao).UseCollation("SQL_Latin1_General_CP1_CS_AS") } static void Collations(){ using var db = new Curso.Data.ApplicationContext(); db.Database.EnsureDeleted(); db.Database.EnsureCreated(); } ``` ### Sequências ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.UseCollation('SQL_Latin1_General_CP1_CI_AI') //STANLEY => stanley //João => Joao modelBuilder.Entity<Departamento>().Property(p=>p.Descricao).UseCollation("SQL_Latin1_General_CP1_CS_AS") //modelBuilder.HasSequence("MinhaSequencia","sequencias"); modelBuilder.HasSequence("MinhaSequencia","sequencias") .StartsAt(1) .IncrementsBy(2) .HasMin(1) .HasMax(10) .IsCyclic(); modelBuilder.Entity<Departamento>() .Property(p=>p.Id) .HasDefaultValuesSql("NEXT VALUE FOR sequencias.MinhaSequencia"); } ``` ### Índices ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder .Entity<Departamento>() .HasIndex(p=> new {p.Descricao, p.Ativo}) .HasDatabaseName("idx_meu_indice_composto") .HasFilter("Descricao IS NOT NULL") .HasFillFactor(80) .IsUnique(); } ``` ### Propagação de dados Sementes de dados ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Estado>() .HasData(new[]{ new Estado{Id = 1, Nome = "São Paulo"}, new Estado{Id = 2, Nome = "Sergipe"}, new Estado{Id = 3, Nome = "Goias"} }); } static void PropagarDados() { using var db = new Curso.Data.ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); var script = db.Database.GenerateCreateScript(); Console.Writeline(script); } ``` ### Esquemas ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("cadastros"); modelBuilder.Entity<Estado>().ToTable("Estados", "SegundoEsquema"); } static void Esquema() { using var db = new Curso.Data.ApplicationContext(); var script = db.Database.GenerateCreateScript(); Console.Writeline(script); } ``` ### Conversosres de valor ```csharp= public class Conversor { public int Id {get; set;} public bool Ativo {get; set;} public bool Excluido {get; set;} public Versao Versao {get; set;} public IPAdress EnderecoIP {get; set;} } public enum Versao { EFCore1, EFCore2, EFCore3, EFCore5, } protected override void OnModelCreating(ModelBuilder modelBuilder) { var conversao = new ValueConverter<Versao, string>(p=>p.ToString(), p=>(Versao)Enum.Parse(typeof(Versao), p)); //Microsoft.EntityFrameworkCore.Storage.ValueConversion.... var conversao1 = new EnumToStringConverter<Versao>(); modelBuilder.Entity<Conversor>() .Property(p=>p.Versao) .HasConversion(conversao1); //.HasConversion(conversao) //.HasConversion(p=>p.ToString(), p => (Versao)Enum.Parse(typeof(Versao), p)); //.HasConversion<string>(); } static void ConversorDeValor() { using var db = new Curso.Data.ApplicationContext(); var script = db.Database.GenerateCreateScript(); Console.Writeline(script); } ``` ### Criando um conversor de valor customizado ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { var conversao = new ValueConverter<Versao, string>(p=>p.ToString(), p=>(Versao)Enum.Parse(typeof(Versao), p)); var conversao1 = new EnumToStringConverter<Versao>(); modelBuilder.Entity<Conversor>() .Property(p=>p.Status) .HasConversion(new ConversorCustomizado()); } public class Conversor { public int Id {get; set;} public bool Ativo {get; set;} public bool Excluido {get; set;} public Versao Versao {get; set;} public IPAdress EnderecoIP {get; set;} } public enum Versao { EFCore1, EFCore2, EFCore3, EFCore5, } public enum Status { Analise, Enviado Devolvido, } public class ConversorCustomizado: ValueConverter<Status, string> { public ConversorCustomizado() : base( p=>ConverterParaOhBancoDeDados(p), value => ConverterParaAplicao(value), new ConverterMappingHints(1)) { } static string ConverterParaOhBancoDados(Status, status) { return status.ToString()[0..1]; } static Status ConverterParaAplicao(string value) { var status = Enum.GetValues<Status>().FirstOrDefault(p=>p.ToString()[0..1] == value); return status; } } static void ConversorCustomizado() { using var db = new ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); db.Conversores.Add( new Conversor{ Status = Status.Devolvido, } ); db.SaveChanges(); Var conversorEmAnalise = db.Conversores.AsNoTraching().FirstOrDefault(p=>p.Status == Status.Analise); Var conversorDevolvido = db.Conversores.AsNoTraching().FirstOrDefault(p=>p.Status == Status.Devolvido); } ``` ### Propriedades de Sombra (Shadaw Properties) ```csharp= Public class Funcionario { public int Id {get;set;} public string Nome {get;set;} //public int DepartamentoId {get;set;} Public Departamento Departamento {get;set;} } static void PropriedadeDeSombra() { using var db = new Curso.Data.ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); } ``` ### Configurando um propriedade de Sombra ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Departamento>().Property<DateTime>("UltimaAtualizacao"); } ``` ### Inserindo e Consultando dados usando uma propriedade de sombra ```csharp= static void TrabalhandoComPropriedadeDeSombra() { using var db = new Curso.Data.ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); var departamento = new Departamento { Descricao = "Departamento Propriedade de Sombra" } db.Departamento.Add(departamento); db.Entry(departamento).Property("UltimaAtualizacao").CurrentValue = DateTime.Now; db.SaveChanges(); var departamentos = db.Departamentos.Where(p=>EF.Property<DateTime>(p, "UltimaAtualizacao") < DateTime.Now).ToArray(); } ``` ### Owned Types ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Cliente>(p=> { p.OwnsOne(x=>x.Endereco, end = > { end.Property(p=>p.Bairro).HasColumnName("Bairro"); end.ToTable("Endereco"); }); }); } Public class Cliente { public int Id {get;set;} public string Nome {get;set;} public string Telefone {get;set;} public Endereco Endereco {get;set;} } public class Endereco { public string Logradouro {get;set;} public string Bairro {get;set;} public string Cidade {get;set;} public string Estado {get;set;} } ``` ### Configurando relacionamento 1x1 ```csharp= Public class Estado { public int Id {get;set;} public string Nome {get;set;} public Governador Governador {get;set;} } public class Governador { public int Id {get;set;} public string Nome {get;set;} public string Idade {get;set;} public string Partido {get;set;} public int EstadoId {get;set;} public Estado Estado {get;set;} } static void Relacionamento1Para1 { using var db = new ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); var estado = new Estado { Nome = "Goiás", Governador = new Governador {Nome = "Stanley Dias"} }; db.Estado.Add(estado); db.SaveChanges(); var estados = db.Estados.Include(p=>p.Governador).AsNotracking().ToList(); estados.ForEach(est => { Console.WriteLine($"Estado: {est.Nome}, Governador: {est.Governador.Nome}"); }); } ``` #### FluentAPI ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { //1-opção //modelBuilder.ApplyConfiguration(new ClienteConfiguration()); //2-opção //modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); //3-opção modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly); } public class ClienteConfiguration : IEntityTypeConfiguration<Cliente> { public void Configure(EntityTypeBuilder<Cliente> builder) { builder.OwnsOne(x=>x.Endereco, end => { end.Property(p=>p.Bairro).HasColumnName("Bairro"); end.ToTable("Enderecos"); }) } } public class EstadoConfiguration : IEntityTypeConfiguration<Estado> { public void Configure(EntityTypeBuilder<Estado> builder) { builder.HasOne(p=>p.Governador) .WithOne(p=>p.Estado) .HasForegnKey<Governador>(p=>p.EstadoId); builder.Navigation(p=>p.Governador).AutoInclude(); } } ``` ### Configurando relacionamento um para muitos ```csharp= Public class Estado { public int Id {get;set;} public string Nome {get;set;} public Governador Governador {get;set;} public ICollection<Cidade> Cidades {get;} = new List<Cidade>(); } public class Governador { public int Id {get;set;} public string Nome {get;set;} public string Idade {get;set;} public string Partido {get;set;} public int EstadoId {get;set;} public Estado Estado {get;set;} } public class Cidade { public int Id {get;set;} public string Nome {get;set;} } public class EstadoConfiguration : IEntityTypeConfiguration<Estado> { public void Configure(EntityTypeBuilder<Estado> builder) { builder.HasOne(p=>p.Governador) .WithOne(p=>p.Estado) .HasForegnKey<Governador>(p=>p.EstadoId); builder.Navigation(p=>p.Governador).AutoInclude(); builder.HasMany(p=>p.Cidades) //.WithOne(p=>p.Estado); or .WithOne() //.OnDelete(DeleteBehavior.Restrict) .IsRequired(false); } } static void Relacionamento1ParaMuitos() { using var db = new ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); var estado = new Estado { Nome = "Goiás", Governador = new Governador {Nome = "Stanley Dias"} }; db.Estado.Cidades.Add(new Cidade {Nome = "Aparecida de Goiânia"}); db.Estado.Add(estado); db.SaveChanges(); using (var db = new ApplicationContext()) { var estados = new db.Estados.ToList(); estados[0].Cidades.Add(new Cidade { Nome = "Goiânia"}); db.SaveChanges(); foreach (var est in db.Estados.Include(p=>p.Cidades).AsNoTracking()) { Console.WriteLine($"Estado: {est.Nome}, Governador: {est.Governador.Nome}"); foreach (var cidade in est.Cidades) { Console.WriteLine($"\t Cidade: {cidade.nome}"); } } } } ``` ### Configurando Relacionamento muitos-para-muitos ```csharp= Public class Ator { public int Id {get;set;} public string Nome {get;set;} public ICollection<Filme> Filmes {get;} = new List<Filme>(); } public class Filme { public int Id {get;set;} public string Descricao {get;set;} public ICollection<Ator> Atores {get;} = new List<Ator>(); } public class AtorFilmeConfiguration : IEntityTypeConfiguration<Ator> { public void Configure(EntityTypeBuilder<Ator> builder) { builder.HasMany(p=>p.Filmes) .WithMany(p=>p.Atores) .UsingEntity(p=>p.ToTable("AtoresFilmes")); builder.HasMany(p=>p.Filmes) .WithMany(p=>p.Atores) .UsingEntity<Dictionary<string, object>( "FilmesAtores", p=>p.HasOne<Filme>.WithMany().HasForeignKey("FilmeId"), p=>p.HasOne<Ator>.WithMany().HasForeignKey("AtorId"), p => { p.Property<DateTime>("CadastradoEm").HasDefaultValueSql("GETDATE()"); } )); } } static void RelacionamentoMuitosParaMuitos() { using var db = new ApplicationContext(); db.DataBase.EnsureDeleted(); db.DataBase.EnsureCreated(); var ator1 = new Ator {Nome = "Stanley"}; var ator2 = new Ator {Nome = "Paulo"}; var ator3 = new Ator {Nome = "Dias"}; var filme1 = new Filme {Descricao = "Estudo e suas consequências"} var filme2 = new Filme {Descricao = "Tecnologia IA"} var filme3 = new Filme {Descricao = "Padroes IT"} ator1.Filmes.Add(filme1); ator1.Filmes.Add(filme2); ator2.Filmes.Add(filme1); filme3.Atores.Add(ator1); filme3.Atores.Add(ator2); filme3.Atores.Add(ator3); db.AddRange(ator1, ator2, filme3) db.SaveChanges(); foreach (var ator in db.Atores.Include(e => e.Filmes)) { Console.WriteLine($"Ator: {ator.Nome}"); foreach (var filme in ator.Filmes) { Console.WriteLine($"\tFilme: {filme.Descricao}" ); } } } ``` ### Campo de Apoio (Backing Field) ```csharp= public class DocumentoConfiguration : IEntityTypeConfiguration<Documento> { public void Configure(EntityTypeBuilder<Documento> builder) { builder .Property(p=>p.CPF) .HasField("_cpf"); // OR builder .Property("_cpf") .HasColumnName("CPF") .HasMaxLength(11); } } ``` ### Configurando modelo de dados com TPH (Tabelas por herança) ```csharp= public class PessoaConfiguration : IEntityTypeConfiguration<Pessoa> { public void Configure(EntityTypeBuilder<Pessoa> builder) { builder .ToTable("Pessoas") .HasDiscrimininator<int>("TipoPessoa") .HasValue<Pessoa>(3) .HasValue<Instrutor>(6) .HasValue<Aluno>(99) } } ``` ### Configurando modelo de dados do TPT (Tabelas por Tipo) ```csharp= public class PessoaConfiguration : IEntityTypeConfiguration<Pessoa> { public void Configure(EntityTypeBuilder<Pessoa> builder) { builder .ToTable("Pessoas") } } public class InstrutorConfiguration : IEntityTypeConfiguration<Instrutor> { public void Configure(EntityTypeBuilder<Instrutor> builder) { builder .ToTable("Instrutores") } } public class AlunoConfiguration : IEntityTypeConfiguration<Aluno> { public void Configure(EntityTypeBuilder<Aluno> builder) { builder .ToTable("Alunos") } } ``` ### Sacola de propriedades (Bolsa de propriedades - Properties bag) ```csharp= protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationContext).Assembly); modelBuilder.SharedTypeEntity<Dictionary<string, object>>("Configuracoes", b => { //Obrigatorio ter uma chave primaria (por convesão EF core entende Id como chave primaria) b.Property<int>("Id"); b.Property<string>("Chave") .HasColumnType("VARCHAR(40)") .IsRequired(); b.Property<string>("Valor") .HasColumnType("VARCHAR(255)") .IsRequired(); }); } class Program { static void PacotesDePropriedades() { using (var db = new Curso.Data.ApplicationContext()) { db.Database.EnsureDeleted(); db.Database.EnsureCreated(); var configuracao = new Dictionary<string, object> { ["Chave"] = "SenhaBancoDeDados", ["Valor"] = Guid.NewGuid().ToString() }; db.Configuracoes.Add(configuracao); db.SaveChanges(); var configuracoes = db .Configuracoes .AsNoTracking() .Where(p => p["Chave"] == "SenhaBancoDeDados") .ToArray(); foreach (var dic in configuracoes) { Console.WriteLine($"Chave: {dic["Chave"]} - Valor: {dic["Valor"]}"); } } } } ```