Comecei no ASP.NET MVC em 2011. A versão era a 3, recém saída e a grande novidade na época era o novo framework de acesso a dados da Microsoft, Entity Framework, que era um desdobramento natural de outro framework da Microsoft, Linq to Entities. Completamente diferente de tudo o que existia na época, o Entity Framework resolvia todos os aspectos chatos e famigerados de se trabalhar com bancos de dados, como a transliteração de registros em objetos, conversões de dados, verificação de integridade referencial e até mesmo organização dos escopos transacionais.

Antes disso, era comum o uso do padrão de repositório para a harmonização entre aplicação e banco de dados. Uma abordagem bastante usada ainda hoje é a de implementar um repositório genérico - algo como Repositorio<T> - com pelo menos 4 métodos: Selecionar(), Incluir(), Atualizar() e Excluir(). O grande problema dela é que nem toda entidade deve selecionar e incluir e atualizar e excluir seus registros, violando alguns princípios do SOLID (o princípio da segregação de interface, principalmente). Ainda assim, o esquema era bastante engenhoso: criava-se um gerador de sentença de consulta (normalmente em SQL) que seleciona os dados de uma entidade e desserializa os registros retornados em objetos do sistema.

Mas, e para selecionar as entidades agregadas?

É a partir deste ponto que iniciam os projetos de frameworks conhecidos como mapeadores objeto-relacional. O primeiro dele, chamado TopLink, foi criado para Smalltalk em 1994, portado para Java e sua última versão foi lançada em 2011. Por muito tempo, o uso desses mapeadores objeto-relacional foi desencorajado por questões de desempenho e uso de memória da aplicação. Em geral, eram lentos e ineficientes, até que surgiu o Hibernate em 2002 e seu conceito de carga preguiçosa de entidades agregadas. Depois disso, todos os frameworks ORM seguiram a tendência, e o Entity Framework não é exceção.

Como desenvolvedor usando Hibernate para o Java e o NHibernate para .NET, conhecia de perto os problemas que eles tinham. Ambos fazem uso de uma sessão de banco de dados que, num ciclo de requisição de uma aplicação MVC, existe apenas durante a execução de um método de Controller, deixando de existir em View. Isto poderia ser driblado através de algumas configurações, mas nunca ficou 100%. Era bastante comum ter que usar Hibernate, ou NHibernate, escrevendo sentenças SQL nativas. Os conceitos de estratégia de carga de entes agregados eram complexos e a usabilidade do código ficava comprometida porque cada tela tinha uma estratégia de carga diferente. Em resumo, não havia um grande ganho em usar esse tipo de tecnologia.

Com o Entity Framework, lançado em 2008, a ideia era resolver alguns desses problemas, e mais alguns outros, como o uso simplificado de transações para persistência.

O que faz ele ser diferente dos demais?

Aliado ao recurso do Linq to Entities, o grande mérito do Entity Framework é ter objetos que funcionem como acumuladores de sentenças, antecipadores de carga e montadores de projeções quase a nível nativo do SQL. Esta resposta do Stack Overflow em Português é um ótimo guia para começar.

Repare que não precisamos escrever SQL para chegar num bom nível de especificidade de SQL. Basta fazer bom uso dos métodos de extensão que o resultado será o mesmo.

O Entity Framework funciona, por padrão, como carga preguiçosa das entidades dependentes, e a antecipação de carga pode ser feita facilmente especificando o método de extensão .Include().

Falando de persistência, os DbSets do Entity Framework trabalham com:

  • .Add() para inserir um objeto no banco de dados;
  • .Remove() para remover um objeto no banco de dados;
  • .Entry(objeto).State = EntityState.Modified para marcar um objeto como modificado, para ser atualizado.

Adição e remoção são em cascata, ou seja, se inserirmos objetos novos como entidades agregadas, o Entity Framework é inteligente o suficiente para inserir esses objetos agregados com a devida integridade referencial.

Para isso, a modelagem da parte de Models precisa ser obrigatoriamente seguindo as melhores práticas para mapeamentos de bancos de dados. Pretendo falar disso em outro texto.

Já conseguiu reparar que um DbSet é um repositório? Não? Então leia isso. Ao terminar, posso finalmente concluir que...

A Era dos Repositórios Chegou ao Fim

Não há mais qualquer motivo para usar repositório com Entity Framework. Pode haver motivo para usar com outra tecnologia, como Dapper, por exemplo, mas o Dapper não prevê carga nativa de entidades agregadas. É preciso escrever tudo na mão.

Há quem diga que uma migração de NHibernate para Entity Framework exija a manutenção de um repositório. Isto não é verdade. NHibernate pede que um repositório seja implementado, mas o uso de um repositório para o Entity Framework fará o Entity Framework trabalhar errado. A resolução de um IQueryable (objeto acumulador de sentenças) em um IEnumerable (lista, coleção, etc.) faz o Entity Framework forçar a pesquisa de dados em banco e trazer todos os resultados em memória. Ou seja, se você está usando Entity Framework em cima de repositório e fará seleção em cima de 20 milhões de linhas, se temos um método public IEnumerable<Entidade> Selecionar(), o retorno poderá ser de 20 milhões de linhas que irão para a memória.

Um último fato é a remoção do Scaffold ControllerWithRepository do ASP.NET MVC5. É sinal de que a própria Microsoft não apóia mais o repositório em projetos ASP.NET MVC.