Breaking changes - EF Core 3.1
Veja como fazer convenções de nomenclatura SnakeCase de forma fácil para o Entity Framework Core 3.1
Este artigo é uma versão atualizada de: https://ralms.io/dica/snakecase/
O objetivo maior deste artigo é mostrar as alterações na API de metadados do Entity Framework Core 3.x.
Veja esse artigo!
Quer ver um resumidão sobre Naming Conventions?
acesse esse link:
https://ralms.io/dica/snakecase/
O que me levou a escrever esse artigo?
Não quero me prolongar aqui, dado que já escrevi um artigo falando sobre minha real necessidade de utilizar conversões de nomenclaturas, então para não ser redundante acessem o link acima e leiam o primeiro artigo que escrevi.
O que mudou, Oops, aliás o que quebrou?
Os métodos de extensão especÃficos do provedor sofreram alterações, nas versões anteriores ao EF 3.0, acessavamos diretamente as propriedades, agora os acessos para algumas dessas propriedades são por métodos, para alguns pode até parecer que ficou mais complicado, mas eu defendo esse tipo de abordagem, em não expor suas propriedades onde as mesmas devem ser acessadas apenas por métodos ou por um construtor, que não é o caso aqui.
Então fiz um DE-PARA aqui do artigo anterior e o que mudou.
Até o EF 2.2 (old)
Tinhamos o seguinte comportamento para acessar e alterar os metadados.
public static void ToSnakeNames(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
entity.Relational().TableName = entity.Relational().TableName.ToSnakeCase();
foreach (var property in entity.GetProperties())
{
property.Relational().ColumnName = property
.Relational()
.ColumnName
.ToSnakeCase();
}
foreach (var key in entity.GetKeys())
{
key.Relational().Name = key.Relational().Name.ToSnakeCase();
}
foreach (var key in entity.GetForeignKeys())
{
key.Relational().Name = key.Relational().Name.ToSnakeCase();
}
foreach (var index in entity.GetIndexes())
{
index.Relational().Name = index.Relational().Name.ToSnakeCase();
}
}
}
private static string ToSnakeCase(this string name)
{
return string.IsNullOrWhiteSpace(name)
? name
: Regex.Replace(
name,
@"([a-z0-9])([A-Z])",
"$1_$2",
RegexOptions.Compiled,
TimeSpan.FromSeconds(0.2)).ToLower();
}
Usando o EF 3.X (new)
Agora o comportamento para acessar os metadados foram alterados.
public static void ToSnakeNames(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
var tableName = entity.GetTableName().ToSnakeCase();
entity.SetTableName(tableName);
foreach (var property in entity.GetProperties())
{
var columnName = property.GetColumnName().ToSnakeCase();
property.SetColumnName(columnName);
}
foreach (var key in entity.GetKeys())
{
var keyName = key.GetName().ToSnakeCase();
key.SetName(keyName);
}
foreach (var key in entity.GetForeignKeys())
{
var foreignKeyName = key.GetConstraintName().ToSnakeCase();
key.SetConstraintName(foreignKeyName);
}
foreach (var index in entity.GetIndexes())
{
var indexName = index.GetName().ToSnakeCase();
index.SetName(indexName);
}
}
}
private static string ToSnakeCase(this string name)
{
return string.IsNullOrWhiteSpace(name)
? name
: Regex.Replace(
name,
@"([a-z0-9])([A-Z])",
"$1_$2",
RegexOptions.Compiled,
TimeSpan.FromSeconds(0.2)).ToLower();
}
Veja como ficou nosso SampleContext
public sealed class SampleContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseNpgsql(
"Host=127.0.0.1;Username=postgres;Password=XXX;Database=TestSnake",
_ => _.EnableRetryOnFailure());
}
}
protected override void OnModelCreating(ModelBuilder modelo)
{
modelo.Entity<TestSnakeCase>();
// Aqui está nossa mágica!
modelo.ToSnakeNames();
//..
}
}
Nossa saÃda SQL
CREATE TABLE test_snake_case (
id serial NOT NULL,
codigo_ibge integer NOT NULL,
nome_completo text NULL,
ano_nascimento integer NOT NULL,
data_cadastro timestamp without time zone NOT NULL,
CONSTRAINT pk_test_snake_case PRIMARY KEY (id)
);
Veja as alterações na API de metadados
Antes | Depois |
---|---|
IEntityType.QueryFilter | GetQueryFilter() |
IEntityType.DefiningQuery | GetDefiningQuery() |
IProperty.IsShadowProperty | IsShadowProperty() |
IProperty.BeforeSaveBehavior | GetBeforeSaveBehavior() |
IProperty.AfterSaveBehavior | GetAfterSaveBehavior() |
IEntityType.Relational().TableName | IEntityType.GetTableName() |
IProperty.Relational().ColumnName | IProperty.GetColumnName() |
IKey.Relational().Name | IKey.GetName() |
IForeignKey.Relational().Name | IForeignKey.GetConstraintName() |
IIndex.Relational().Name | IIndex.GetName() |
Performance
1 - Em vez de usar Task
2 - Você poderia até não saber disso, mas quando acessavamos o método Entry() era disparado um DetectChanges() para todos objetos daquele contexto especÃfico, agora isso não é mais uma verdade :) ualll!
Código Completo!
using Microsoft.EntityFrameworkCore;
using System;
using System.Text.RegularExpressions;
namespace SnakeCase
{
class Program
{
static void Main(string[] args)
{
using var db = new SampleContext();
var script = db.Database.GenerateCreateScript();
Console.WriteLine(script);
}
}
public sealed class SampleContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseNpgsql(
"Host=127.0.0.1;Username=postgres;Password=XXX;Database=TestSnake",
_ => _.EnableRetryOnFailure());
}
}
protected override void OnModelCreating(ModelBuilder modelo)
{
modelo.Entity<TestSnakeCase>();
// Aqui está nossa mágica!
modelo.ToSnakeNames();
}
}
public static class SnakeCase
{
public static void ToSnakeNames(this ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
var tableName = entity.GetTableName().ToSnakeCase();
entity.SetTableName(tableName);
foreach (var property in entity.GetProperties())
{
var columnName = property.GetColumnName().ToSnakeCase();
property.SetColumnName(columnName);
}
foreach (var key in entity.GetKeys())
{
var keyName = key.GetName().ToSnakeCase();
key.SetName(keyName);
}
foreach (var key in entity.GetForeignKeys())
{
var foreignKeyName = key.GetConstraintName().ToSnakeCase();
key.SetConstraintName(foreignKeyName);
}
foreach (var index in entity.GetIndexes())
{
var indexName = index.GetName().ToSnakeCase();
index.SetName(indexName);
}
}
}
private static string ToSnakeCase(this string name)
{
return string.IsNullOrWhiteSpace(name)
? name
: Regex.Replace(
name,
@"([a-z0-9])([A-Z])",
"$1_$2",
RegexOptions.Compiled,
TimeSpan.FromSeconds(0.2)).ToLower();
}
}
public class TestSnakeCase
{
public int Id { get; set; }
public int CodigoIBGE { get; set; }
public string NomeCompleto { get; set; }
public int AnoNascimento { get; set; }
public DateTime DataCadastro { get; set; }
}
}
Os fontes do exemplo usado está aqui:
https://github.com/ralmsdeveloper/samplesnakecase
#mvpbuzz #mvpbr #mvp #developerssergipe #share #vscode #postgresql #efcore31 #netcore31
Deixe um comentário