Entity Framework: Code-First, Database-Never


Conheço desenvolvedores que gostam do Entity Framework (EF), mas nunca vi um DBA que fale bem dele.

Para explicar parte desse fenômeno, quero dar um exemplo baseado no projeto do ARDA (https://github.com/dxbrazil/arda).

[UPDATE 12/07] Obrigado ao André Baltieri por gravar um vídeo com uma explicação tão didática. Depois de assistir ao vídeo, já estou entrando em contato para adquirir o curso sobre Entity Framework. Quem sabe agora posso escrever um EF decentemente. Cuidado que o vídeo tem SPOILER.

Code-First

Ao usar o code-first, as tabelas são geradas automaticamente a partir das classes modelos escritas em C#. Isso permite que o desenvolvedor fique focado dentro do Visual Studio sem precisar abrir o SQL Management Studio para acessar o banco de dados. Na verdade, não é preciso escrever uma linha de script T-SQL.

Durante o desenvolvimento, usamos o Entity Framework Core (EF Core) e adotamos o code-first para modelar nosso banco de dados.

Eu juro que a ideia não foi minha – embora o histórico do Git diga o oposto!

[fcatae committed on Apr 26, 2016] Configure the database using EF migrations
https://github.com/DXBrazil/Arda_old/commit/b00a4f2943973e2ddb2a734e1e5b3640503ae05a

A ideia de code-first é interessante em um caso primeiro momento. Por exemplo, é ótimo para escrever tutoriais:

[Entity Framework Core] ASP.NET Core - New database with Visual Studio 2017
https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/new-db

[Entity Framework Core] Getting Started on .NET Core
https://docs.microsoft.com/en-us/ef/core/get-started/netcore/

Ao meu ver, as vantagens acabam por aí.

Database-Never

O problema em adotar uma estratégia de code-first é que o desenvolvedor não valida o conteúdo gerado no banco de dados.

Por exemplo, no ARDA temos uma tabela principal chamada WorkloadBacklogs. Os itens registrados no sistema podem ser classificados como Workload ou Backlog.

image

Do ponto de vista de modelagem de dados, a nomenclatura é estranha.

  • Por que todas as colunas começam com “WB”? WBID, WBActivityActivityID, WBCreatedDate, WBDescription, etc…
  • Por que a coluna “WBActivityActivityID” não poderia ser chamada apenas de “ActivityID”?
  • Por que a coluna “WBCreatedDate” armazena datas com precisão de microssegundos? 2017-01-31 20:55:02.5101859

Outro exemplo é a tabela WorkloadBacklogTechnologies, que já tem um nome curioso.

image

As colunas possuem os nomes de WBUTechnologyID, TechnologyTechnologyID e WorkloadBacklogWBID.

E o que seriam esses campos de UniqueIdentifier?

UniqueIdentifier

O tipo de dados UniqueIdentifier (também conhecidos como GUID) é um valor de 16-bytes representado no formato:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Os identificadores GUID foram muito usados em componentes COM/COM+ devido à garantia de que eles são únicos mesmo em ambientes descentralizados. Entretanto, é muito estranho analisar uma tabela com esses campos, pois os dados parecem criptografados:

image

Em um banco de dados, os campos UniqueIdentifier são desencorajados:

  • Tipo de dados INTEGER é mais compacto e ocupa apenas 32-bits
  • Campos UniqueIdentifier são aleatórios e causam fragmentação de tabela

Resumindo: DBA’s preferem campos INTEGER com IDENTITY.

NVARCHAR(max)

Existem dois tipos de NVARCHAR: limitado e ilimitado.

Em geral, as tabelas devem usar campos NVARCHAR especificando o tamanho máximo em caracteres. Isso permite que o banco de dados mantenha todos os dados dentro do mesmo bloco físico (8kb) do disco.

Porém, observamos exatamente o oposto na definição da tabela.

image

Se você usa o Entity Framework com code-first, é muito provável que os campos string estejam mapeados como NVARCHAR(max). Esse tipo de dado aumenta a fragmentação da tabela sem necessidade.

Próximo Artigo

As esquisitisses do Entity Framework não acabam por aqui. No próximo post, pretendo me aprofundar um pouco mais nos problemas que tivemos ao longo do projeto.

UPDATE: Gostaria de agradecer a todos que deixaram criticas ao artigo, pois todo comentário adiciona contexto ao texto original. O objetivo é mostrar motivos pelos quais "DBAs odeiam o EF", mesmo sabendo que a culpa é do desenvolvedor ao invés do framework. Esse é um exemplo real que aconteceu dentro da nossa própria casa (https://www.github.com/dxbrazil/arda). Posso certificar que esse descuido não foi exclusivo nosso, mas que acontece em vários outros lugares. Não deixem de acompanhar os próximos artigos e deixar seus comentários!

 


Comments (20)

  1. Boas Catae, é muito interessante ver a visão de um DBA sobre ORM, temos pouco essa discussão, na maioria das vezes falamos de DEV para DEV, então é muito bem vindo os próximos artigos.

    Só gostaria de salientar algumas coisas aqui, que ao meu ver acabou passando batido na configuração do EF, e causando estes “desconfortos” na visão do DBA.

    Por que todas as colunas começam com “WB”? WBID, WBActivityActivityID, WBCreatedDate, WBDescription, etc…
    R: Isto pode ser mapeado através do ToTable() no arquivo de mapeamento, onde você escolhe o nome da tabela.

    Por que a coluna “WBActivityActivityID” não poderia ser chamada apenas de “ActivityID”?
    R: O nome da coluna também pode ser mapeado para chamar apenas ActivityId

    Por que a coluna “WBCreatedDate” armazena datas com precisão de microssegundos? 2017-01-31 20:55:02.5101859
    R: Novamente um problema de mapeamento, utilizando o Type no mapeamento, você consegue definir o tipo do campo.

    UniqueIdentifier
    R: Sobre o caso de UI, é mais interessante para o desenvolvedor gerar o ID da entidade no código, isso facilita os testes por exemplo. Seguindo um modelo onde temos um domínio ríco, a regra fica toda na aplicação, e gerar um identificador único pelo C# facilita isto.

    NVARCHAR(max)
    R: Novamente um ponto que pode ser resolvido utilizando o Type no mapeamento.

    1. Baltieri, sua opinião é muito importante. Antes de falar sobre os detalhes técnicos, deixo comentar sobre o objetivo do artigo.
      * “Se a configuração correta do EF fosse feita, nada disso aconteceria” – verdade.
      * “DBA odeiam o entity framework” – verdade.
      Posso concluir que “DBA odeia o entity framework porque muitas vezes o EF não foi configurado corretamente”?
      O objetivo desse post é provocar exatamente essa discussão: nem sempre o desenvolvedor faz o melhor uso do banco de dados quando usa o EF com Code-First. Além disso (próximo post), as facilidades do EF podem enganar o desenvolvedor, mas não o DBA que enxerga a query.
      Sobre os pontos técnicos que voce colocou – sim, tudo isso é verdade. Infelizmente demoramos muito tempo para conseguir detectar esse problema e agora está quase impossível corrigir em produção. Se você olhar no GitHub, vai notar que criamos um projeto de Integration Test especificamente para validar o EF. O objetivo era fazer a correção do schema do banco de dados com a garantia que os testes de integração continuassem válidos. Daí surgiu uma segunda dúvida: vale a pena investir tempo e esforço nisso?
      Obrigado pelo comentário. Abraços, Fabricio

    2. Ah.. sobre GUID… use INT IDENTITY.
      (merece um post?)

  2. Leandro disse:

    But all the “problems” mentioned are easily configured.

    1. Good point. “Easily” configured for those who knows it. But we don’t (LOL) – that is our current production code.
      Now we are working to fix it, but that’s not easy at all. I can provide more information on why we are NOT fixing it for next week.
      I agree that it would be easy to fix in the beginning of the project though.
      Thanks, Fabricio

  3. Douglas disse:

    Todos os pontos identificados seriam resolvidos diretamente no modelBuilder do EF. Faltou conhecer melhor o ORM.

    1. Oi Douglas, voce está certo. Baltieri tbem detalhou todos os pontos. Agora a pergunta é como resolver o problema dessa aplicação que está em produção?
      Abraços, Fabricio

      1. Então na verdade faltou a validação do DBA logo após a criação do banco com code-first, querer “culpar” o ORM por agora estar impossível corrigir em produção é um “crime” não acha?

        1. Oi Cleber! Vamos discutir mais sobre isso?
          Eu acho que é impossível corrigir em produção hoje.. qual alternativa que você ve?
          Obrigado, Fabricio

          1. Alexandre Borges disse:

            Catae,
            acho essa questão um tanto sensível. Ela deveria ter sido notada e resolvida antes de entrar em produção, onde teria um custo infinitamente menor. Portanto, acho que a resposta para essa questão está mais para responder outras questões: Custo, recursos e tempo. Tecnicamente, é possível resolver em produção, mas o que pesa são esses três pilares. Mas sim, o seu ponto de vista como o perigo de cair em armadilhas usando ORMs é muito grande mas, assim como cair numa armadilha dessas tee causa em desconhecimento, questões técnicas ou outras questões que não saberia dizer agora (prazo, custo, etc), não usar ORM tb poderia acarretar. No fim, é uma questão de planejamento e gestão do projeto.

          2. Obrigado pelo seu comentário. Isso reforça o ponto de vista que o ORM nao é o problema.
            Concordo que na teoria ela deveria ter sido resolvida antes de entrar em produção, porém, na prática, aconteceu o inverso. E agora? O objetivo do post é mostrar a realidade e ilustrar uma situação possível de acontecer (por culpa do desenvolvedor). Na verdade, já evolui um pouco mais a análise dos problemas que tivemos com o ARDA, mas tive pouco tempo para detalhar no blog.
            Abraços, Fabricio

  4. Obrigado pelos comentários! Fico bem feliz de ouvir a opinião concordando ou discordando.
    Antes de falar qualquer coisa, deixo dizer que: “se você é uma pessoa experiente em EF (ou em qualquer tecnologia), então não enfrentará problemas”. Em outras palavras, “se você fizer certo, então estará certo”.
    Infelizmente nosso time está errado e fizemos errados. Estamos compartilhando publicamente nossas mancadas.

  5. Lucilvio Lima disse:

    Gostei do artigo por expor problemas comuns encontrados no uso do EF com Code First, entendi a provocação que o artigo pretende causar, então nao preciso dizer que o Baltieri foi certeiro na colocação dele. O problema e que a própria Microsoft em alguns de seus tutoriais comete o erro de nao expor esses tipos de armadilhas do uso indiscriminado do Code First, e ela faz isso com muitas das suas outras tecnologias, acho que esse eh um caso onde sua provocação cabe bem.

  6. José disse:

    Olá Catae!

    A sensação ao ler o artigo é que o EF seja o vilão da história. E não é. Todos esses pontos poderiam ser facilmente contornados fazendo mapeamento das entidades de forma correta. Lembrando que para o desenvolvedor, o uso de orm’s só aumentam a produtividade. O ideal seria expor os dois lados. O problema e a solução.

    1. Oi José! Obrigado pelo seu comentário. Essa é nossa aplicação em produção e o objetivo do artigo é mostrar que nem sempre tudo é bonito.

      Começamos o projeto com o EF e rapidamente já estava tudo pronto (é só ver o histórico de commits do projeto), então não vou discutir a questão de produtividade. Por outro lado, tivemos muitos problemas de desempenho e mostro alguns exemplos nos próximos artigos. Só conseguimos enxergar esses problemas quando investigamos o banco de dados a fundo. Tenho certeza que várias aplicações passam pelo mesmo problema que nós passamos (e algumas nem sabem o motivo).

      Hoje nosso projeto continua rodando com o EF Core e não temos planos para mudar.

      Obrigado, Fabricio

    2. Se quiser, ao invés de culpar o EF, pode culpar o desenvolvedor! Sou eu! 🙂
      Vou ser bem sincero: não ligo de dizer que erramos feio, porque no final aprendemos muito com isso! Hoje conseguimos compartilhar esses problemas junto a comunidade.

      1. José disse:

        Mas isso que é o legal, Catae! Estamos sempre aprendendo e compartilhando nossos conhecimentos. Tenho a absoluta certeza que no próximo projeto todos esses itens serão lembrados e melhorados! Um abraço

  7. Edmilson Miyasaki disse:

    Em 2009 quando estava desenvolvendo um projeto utilizando Scrum, nosso DevTeam tinha uma pessoa que conhecia muito de banco de dados, embora não fosse um DBA propriamente dito. Com a ajuda dele, refizemos toda a configuração do Hibernate para que as tabelas e queries fossem geradas da forma mais eficiente possível.

    Então, fugindo um pouco dos comentários sobre configuração do EF, eu considero importantíssimo a participação de um DBA — além de pessoas com outros skills, mesmo que seja somente nas reuniões de planejamento — para não corrermos o risco de desenvolver algo e somente muito à frente descobrirmos que temos uma falha no produto que poderia ter sido evitada.

    Abraços.

    1. Concordo… Infelizmente colocar um DBA para ajudar nem sempre é solução. Tem vezes que o próprio DBA se torna um problema, deixando o processo mais lento ao invés de mais ágil. Não tenho dúvida que colocar pessoas de Dev com conhecimento de banco de dados sempre ajuda na solução. Como você disse, pode ser no ajuste das configurações do Hibernate. Obrigado pelo comentário!
      Abraços, Fabricio

Skip to main content