Freigeben über


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.

https://www.youtube.com/watch?v=EmSIlaPKzGM

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

  • Anonymous
    July 11, 2017
    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 ActivityIdPor que a coluna “WBCreatedDate” armazena datas com precisão de microssegundos? 2017-01-31 20:55:02.5101859R: Novamente um problema de mapeamento, utilizando o Type no mapeamento, você consegue definir o tipo do campo.UniqueIdentifierR: 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.
    • Anonymous
      July 11, 2017
      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
    • Anonymous
      July 11, 2017
      Ah.. sobre GUID... use INT IDENTITY.(merece um post?)
  • Anonymous
    July 11, 2017
    But all the "problems" mentioned are easily configured.
    • Anonymous
      July 11, 2017
      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
  • Anonymous
    July 11, 2017
    Todos os pontos identificados seriam resolvidos diretamente no modelBuilder do EF. Faltou conhecer melhor o ORM.
    • Anonymous
      July 11, 2017
      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
      • Anonymous
        August 02, 2017
        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?
        • Anonymous
          August 03, 2017
          Oi Cleber! Vamos discutir mais sobre isso?Eu acho que é impossível corrigir em produção hoje.. qual alternativa que você ve?Obrigado, Fabricio
          • Anonymous
            November 12, 2017
            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.
            • Anonymous
              November 13, 2017
              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
  • Anonymous
    July 11, 2017
    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.
  • Anonymous
    July 11, 2017
    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.
  • Anonymous
    July 12, 2017
    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.
    • Anonymous
      July 12, 2017
      The comment has been removed
    • Anonymous
      July 12, 2017
      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.
      • Anonymous
        July 12, 2017
        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
        • Anonymous
          July 12, 2017
          Obrigado!!!
  • Anonymous
    July 21, 2017
    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.
    • Anonymous
      July 28, 2017
      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