Pontos de dados

Dicas para a atualização e refatoração do seu código do Entity Framework

Julie Lerman

Julie LermanEu tenho trabalhado com muitos clientes para ajudar a refatorar os softwares existentes que utilizam o Entity Framework (EF). No caso do código do EF, a refatoração pode ter uma variedade de significados e muitas vezes envolvem uma atualização. Nesta coluna, vou analisar algumas das maneiras com as quais você pode estar revisando o seu código do EF, ou as aplicações que o envolvem. Para cada uma destas abordagens, fornecerei alguma orientação, com base em minhas experiências de trabalho com aplicativos de produção dos clientes, que deverão ajudá-lo a estar bem preparado e evitar algumas dores de cabeça.

As alterações que vamos discutir são:

  • Atualizando para uma nova versão do Entity Framework
  • Quebrando um grande Modelo de Dados do Entity
  • Substituindo a API do ObjectContext com a API do DbContext

Falarei sobre as duas primeiras, em termos gerais, durante este mês e, em seguida, na minha próxima coluna, aprofundar-me-ei no último cenário com orientações e alguns exemplos concretos.

Antes de embarcar em qualquer uma dessas tarefas, há um conselho que eu recomendo que você siga: Fazê-las uma de cada vez. Eu tenho participado de empreendimentos que incluem um projeto antigo que utiliza o EF4, um modelo enorme e a API do ObjectContext. Tentar alterar as três ao mesmo tempo só pode levar a lágrimas e frustração ou, talvez, coisas piores. Neste caso, a minha conduta sugerida era, primeiro, atualizar a versão do EF sem fazer quaisquer outras alterações e, em seguida, certificar-se de que tudo continua funcionando. A etapa seguinte envolveu a identificação de uma área do modelo que poderia ser extraída para um novo modelo pequeno. Mas, inicialmente, eu deixei o novo modelo continuar a mirar a API do ObjectContext. Quando tudo estava em ordem de trabalho, a alteração para a API do DbContext foi iniciada, mas apenas para o modelo pequeno. Caso contrário, muitas coisas poderiam ser quebradas em todo o aplicativo e você estaria em uma missão incoerente e selvagem para caçar uma variedade de bugs. Ao mudar um modelo pequeno de cada vez, você tem uma área de superfície menor de códigos quebrados para trabalhar e você pode aprender algumas boas lições e padrões que vão fazer com que a alteração do próximo modelo pequeno seja muito menos dolorosa.

Atualizando para uma versão mais nova do Entity Framework

Graças ao foco da equipe do EF na compatibilidade com as versões anteriores, mover-se de uma versão para outra não proporciona muito atrito. Agora, focalizarei na atualização para o EF6 - a versão principal do EF6, bem como suas atualizações menores, como o EF6.02 e o EF6.1.

Os piores problemas ao deslocar-se do EF4, EF4.1 ou EF5 para o EF6 (que, na minha opinião, realmente não são tão ruins) resultam de algumas alterações de namespace. Como as APIs originais ainda estão no Microsoft .NET Framework., tê-las duplicadas no EF6 causaria um problema. Portanto, na API do EF6, estas classes estão em um namespace diferente do System.Data para evitar conflitos. Por exemplo, há um certo número de namespaces nas APIs baseadas em .NET do EF que começam com System.Data.Objects, System.Data.Common, System.Data.Mapping, System.Data.Query, assim como algumas outras. Há também algumas classes e enumerações que vivem diretamente no System.Data; por exemplo, System.Data.EntityException e System.Data.Entity­State. A maioria das classes e namespaces que estavam diretamente ligados ao System.Data neste meio foram movidos para a nova raiz do namespace, System.Data.Entity.Core. Há um punhado de classes movidas para o System.Data.Entity, como a EntityState, que agora se encontra em System.Data.Entity.EntityState. Por exemplo, o namespace de Mapeamento agora é System.Data.Entity.Core.Mapping, enquanto o namespace de Objetos agora é System.Data.Entity.Core.Objects. Veja o item 4 na documentação do Data Developer Center, "Atualizando para o EF6" (bit.ly/OtrKvA), para as exceções específicas que não entram no System.Data.Entity.Core.

Ao atualizar aplicativos existentes para o EF6, eu apenas deixo o compilador realçar as mudanças, mostrando-me todos os erros do gênero "O tipo ou o namespace... não foi encontrado" e, em seguida, realizo alguma localização e substituição para corrigir os namespaces.

Como exemplo, eu comecei com uma pequena solução de amostra a partir da segunda edição do meu livro, "Programming Entity Framework". Esta solução foi escrita usando o EF4, um modelo EDMX, classes de entidade POCO geradas por código e a do API ObjectContext. Na época, o Code First e a API do DbContext não existiam. Antes de começar, eu verifiquei se o aplicativo ainda estava funcionando (depuração no Visual Studio 2013).

Embora eu esteja focando um maior salto - do EF4 para o EF6- você precisa seguir o mesmo caminho de correções de namespace se você está passando do EF5 para o EF6, porque essas alterações de namespace ocorreram entre o EF5 e o EF6. Mover-se do EF4 diretamente para o EF6 envolve alguns desafios extras, além disso, a minha solução antiga usou um modelo T4, o qual não conta um substituto direto.

Meus passos para atualizar do EF4 para o EF6

Se você está acostumado a ter o Entity Framework usando a distribuição de pacotes NuGet, você precisa pensar que antes o EF era simplesmente parte do .NET Framework e todas as suas DLLs viviam no GAC (Cache de Assembly Global). Antes de atualizar para o EF6, removi manualmente as referências ao System.Data.Entity (versão 4.0.0.0) em cada um de meus projetos de solução. Eu também limpei a solução (clicando com o botão direito do mouse na solução, no Gerenciador de Soluções, e escolhendo Solução Limpa) para me assegurar de que nenhuma das DLLs originais que eu possa ter forçado para as pastas BIN tenha desaparecido. Acontece que a minha diligência não era necessária, porque o instalador de pacotes NuGet para EF6 remove as antigas referências para você.

Em seguida, eu usei o NuGet para instalar o EF6 nos projetos relevantes e recriei a solução. As dependências do projeto em suas próprias soluções conduzirão a rapidez com que as questões de namespace surgirão. Com a minha solução, só foi mostrado um erro de namespace no início. Eu o reparei e recriei a solução e, em seguida, vi muitos erros - todos, exceto um, eram problemas de namespace. Para a única exceção, o compilador forneceu uma mensagem útil ao dizer-me que um atributo não muito usado que eu tinha aproveitado (EdmFunction) havia sido submetido a uma mudança de nome (e, portanto, foi marcado como Obsoleto) e tinha sido substituído pelo atributo DbFunction. 

Após uma série de correções e recriações iterativas de namespace, as quais duraram apenas alguns minutos para esta pequena solução, eu fui capaz de criar e executar o aplicativo - visualizar, editar e salvar dados.

Corrigindo o ObjectContext mais o modelo T4 POCO

Há uma outra tarefa possível para ter em mente. Minha solução usou um EDMX - um modelo de Entity Data concebido e mantido com o EF Designer. Devido ao fato de eu tê-la criado no Visual Studio 2010 com o EF4, ela se baseou em um modelo mais antigo de geração de código que criou um ObjectContext para gerenciar toda a persistência de dados e cache. Esse modelo gerou classes POCO (que não possuem dependência com o Entity Framework) a partir das entidades do modelo. Se eu fizer qualquer alteração no meu modelo, precisarei regenerar as classes e o contexto. Porém, esse modelo mais antigo, o qual gera o contexto, não tem consciência dos novos namespaces. Embora existam modelos DbContext e ObjectContext (além de EntityObjects) (ver Figura 1), não há substituto para o meu modelo que fornece a combinação ObjectContext mais POCO. Além disso, para tornar as coisas mais interessantes, eu tinha personalizado o modelo que usei. Então, em vez de selecionar um novo modelo que não funcionaria com o meu pedido, eu fiz duas pequenas alterações no modelo Context.tt que foi armazenado na minha solução existente:

  1. Na linha 42, "using System.Data.Objects;" se torna "using System.Data.Entity.Core.Objects;"
  2. Na linha 43, "using System.Data.EntityClient;" se torna "using System.Data.Entity.Core.EntityClient;"

You’ll Find Templates to Generate DbContext Plus POCOs or ObjectContext Plus Non-POCOs.
Figura 1 Você encontrará modelos para gerar DbContext mais POCO ou ObjectContext sem POCO.

Agora, toda vez que eu regenerar as classes do modelo, a classe ObjectContext obterá os namespaces corretos e as minhas classes geradas em POCO personalizadas continuarão a funcionar no meu aplicativo. Observe que a documentação do Data Developer Center que eu mencionei anteriormente explica como usar os modelos suportados.

Benefícios que você ganhará sem alterar mais nenhum código

Eu achei a atualização do aplicativo para usar o EF6 bastante simples. No entanto, há um ponto muito importante a considerar. Enquanto o aplicativo está usando a versão mais recente do Entity Framework, a atualização é benéfica apenas por causa das melhorias subjacentes ao Entity Framework, em particular, alguns grandes ganhos de desempenho que vieram no EF5 e no EF6. Como a maior parte desses ganhos de desempenho surgiram no EF5, a mudança do EF5 para o EF6, sem o aproveitamento dos outros novos recursos, não terá tanto impacto. Para ver o que mais há de novo no EF6, confira o meu artigo de dezembro de 2013 "Entity Framework 6: A edição Ninja" (bit.ly/1qJgwlf). Muitas das melhorias são recursos relacionados com a API do DbContext e Code First. Se você deseja atualizar para o EF6 e também atualizar o DbContext, eu recomendo começar com a simples atualização para o EF6 e, antes de mudar para a API do DBcontext, confirmar que tudo esteja funcionando novamente. Esta é uma mudança mais complexa, a qual abordarei detalhadamente na minha próxima coluna.

Mesmo com essas mudanças possivelmente mínimas, sua base de códigos está pronta para aproveitar as APIs mais modernas.

Quebrando um modelo grande

Mesmo que você já tenha usado o EF Designer para criar o seu modelo ou o fluxo de trabalho do Code First (ver "Desmistificando estratégias do Entity Framework: Fluxo de trabalho de criação de modelos" em bit.ly/Ou399J), modelos com muitas entidades em si podem causar problemas de tempo de design e até mesmo de tempo de execução. Minha experiência pessoal é de que os grandes modelos são muito pesados e difíceis de manter. Se você estiver usando o Designer e tiver muitas entidades (centenas), não só levará mais tempo para o designer abrir e exibir o modelo, como também será mais difícil de navegar visualmente no modelo. Felizmente, o Designer ganhou alguns ótimos recursos no Visual Studio 2012 que são de grande ajuda (ver "Entity Framework Designer ganha afeição no Visual Studio 2012" em bit.ly/1kV4vZ8).

Mesmo assim, eu sempre recomendo que os modelos sejam menores. Na minha coluna, "Reduza os modelos EF com Contextos Limitados DDD" (bit.ly/1isIoGE), eu falo sobre os benefícios dos modelos menores, bem como de algumas estratégias para usá-los no seu aplicativo. Se você já tem um modelo grande, no entanto, pode ser um desafio quebrá-lo. Eu tive clientes que estavam trabalhando com modelos nos quais eles fizeram engenharia reversa de enormes bancos de dados, resultando em 700 ou até 1.000 entidades. Se você está encolhendo um modelo EDMX no Designer ou usando o Code First, a história é a mesma: Quebrá-lo será difícil.

Aqui estão algumas dicas úteis para quebrar um modelo grande em modelos menores para uma manutenção mais simples e potencialmente melhor desempenho em tempo de execução.

Não tente refatorar todo o modelo de uma vez só. Para cada modelo pequeno que você extrair, você precisará fazer alguma refatoração de código adicional, pois as referências mudam e você pode ter um código de relacionamento para enredar também.

Portanto, o primeiro passo é identificar uma seção do modelo que seja quase autônoma. De início, não se preocupe com a sobreposição. Por exemplo, você pode estar trabalhando em um sistema para uma empresa que fabrica e vende produtos. O software pode incluir um recurso para a manutenção da sua força de vendas - dados pessoais, informações de contato, território de vendas, etc. Outra parte do software pode referenciar as pessoas de vendas ao criar um pedido de cliente com base na definição de seus territórios de vendas. Ainda, uma outra parte pode estar acompanhando as suas comissões de vendas. Portanto, resolva um cenário particular de cada vez - por exemplo, manutenção da lista do vendedor.

Aqui estão as etapas para a alteração de modelos menores:

  1. Identifique as entidades envolvidas nesse cenário (ver Figura 2).
  2. Crie um projeto completamente novo.
  3. Neste projeto, defina um novo modelo (quer seja com o EF Designer ou o Code First), que esteja ciente das entidades competentes.
  4. Identifique o código do aplicativo envolvido com a manutenção do produto.
  5. Atualize o código relevante que usa o contexto original (para consultar, guardar as alterações ou outras funcionalidades) para que ele use o novo contexto que você criou.
  6. Refatore o código existente até que a funcionalidade de destino esteja funcionando. Se você tiver testes automatizados no lugar, isso pode tornar a tarefa de refatoração mais eficiente.

Alguns conselhos adicionais para se ter em mente durante este processo:

  • Não retire estas entidades do modelo grande. Ao fazer isso, muitas coisas seriam quebradas. Basta deixá-las lá e esquecê-las, por enquanto.
  • Mantenha um registro do tipo de refatoração que você teve de fazer para obter o aplicativo em funcionamento com o novo modelo menor.

SalesPerson and Territory Maintenance Can Be Extracted into a Separate Model with Little Impact to Other Entities
Figura 2 A manutenção de vendedor e de território pode ser extraída em um modelo separado, com pouco impacto para as outras entidades

Você aprenderá padrões que poderá aplicar se você continuar a quebrar outros modelos focados do grande modelo.

Ao adicionar iterativamente um novo modelo pequeno de cada vez à sua solução e dirigir o código para usá-lo, sua experiência será muito mais agradável. Na modelo grande, estas mesmas entidades podem ser enredadas em relacionamentos que não têm nada a ver com a tarefa de manutenção de vendedor. Por exemplo, você pode ter uma relação de vendedor com um pedido de venda, então, você provavelmente não vai querer remover o vendedor completamente do modelo. É mais simples ter um tipo de vendedor reduzido ou de apenas leitura como referência durante a criação do pedido em um modelo e, em seguida, o tipo completo de vendedor (sem o conhecimento dos pedidos) para manutenção em outro modelo. Quando tudo estiver pronto, as duas entidades podem continuar apontando para o mesmo banco de dados. Se você estiver usando o Code First e migrações, verifique o artigo mencionado acima sobre o encolhimento de modelos EF como orientação sobre o compartilhamento de um banco de dados quando você tem vários modelos que se sobrepõem.

Eventualmente, você pode acabar com muitos modelos pequenos e, ainda, algumas referências ao modelo grande. Nesse ponto, será mais fácil identificar quais entidades não são mais tocadas no modelo grande e podem ser removidas com segurança.

Paciência: Sim, é uma virtude

A conclusão mais importante é abordar essas atualizações e refatorações em pequenas partes e, em cada iteração, obter seus testes e aplicativo em funcionamento novamente antes de prosseguir. Considere a atualização para uma versão mais recente do Entity Framework como o seu próprio item de trabalho. E mesmo que isso possa ser dividido em dois, já que você se concentra primeiro nas APIs mais recentes, certificando-se de que o seu código continua funcionando antes de alterar o código para tirar proveito dos novos recursos. Os mesmos passos se aplicam à divisão de um modelo grande, especialmente se você estiver planejando atualizar do ObjectContext para o DbContext. Extraia modelos pequenos e refatore para certificar-se de que a lógica relevante pode funcionar com o novo modelo menor. Uma vez que esteja funcionando, é hora de romper os seus laços com o ObjectContext, o qual quebrará uma boa quantidade de códigos antes. Mas, pelo menos, esses códigos quebrados serão colocados em quarentena para uma área menor da sua base de códigos, e você vai ter de refatorar cada vez menos códigos.

Na minha próxima coluna, vou me aprofundar no objetivo mais doloroso, porém atingível, de mover o código de ObjectContext para usar a API do DbContext.

Julie Lerman é MVP da Microsoft, mentora e consultora do .NET, que reside nas colinas de Vermont. Você pode encontrá-la fazendo apresentações sobre acesso a dados e outros tópicos do .NET em grupos de usuários e conferências em todo o mundo. Seu blog está em thedatafarm.com/blog e é autora do livro “Programming Entity Framework” (2010), além das edições Code First (2011) e DbContext (2012), todos da O’Reilly Media. Siga Julie no Twitter em twitter.com/julielerman e confira seus cursos da Pluralsight em juliel.me/PS-Videos.

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: Rowan Miller