###### 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"]}");
}
}
}
}
```