Repositório genérico Com o EF 4.1, qual é o objetivo

145

À medida que me aprofundo no DbContext, no DbSet e nas interfaces associadas, fico imaginando por que você precisaria implementar um repositório "genérico" separado em torno dessas implementações?

Parece que o DbContext e o IDbSet fazem tudo o que você precisa e inclui a "Unidade de trabalho" dentro do DbContext.

Estou faltando alguma coisa aqui ou parece que as pessoas gostam de adicionar outra camada de dependência sem motivo.

Code Jammr
fonte
Este é um assunto pouco disputado / baseado em opiniões. Eu discuti isso aqui .
Amit Joshi

Respostas:

202

Você está realmente certo. DbContexté uma implementação do padrão de unidade de trabalho e IDbSeté uma implementação do padrão de repositório.

Atualmente, os repositórios são muito populares e usados ​​em excesso. Todos os usam apenas porque existem dezenas de artigos sobre a criação de repositório para a estrutura da entidade, mas ninguém descreve os desafios relacionados a essa decisão.

Os principais motivos para usar o repositório são geralmente:

  • Ocultar EF da camada superior
  • Tornar o código melhor testável

A primeira razão é algum tipo de pureza arquitetônica e uma ótima idéia de que, se você tornar suas camadas superiores independentes do EF, poderá posteriormente mudar para outra estrutura de persistência. Quantas vezes você viu isso no mundo real? Esse motivo torna o trabalho com o EF muito mais difícil, porque seu repositório deve expor muitos recursos adicionais, abrangendo o que o EF permite por padrão.

Ao mesmo tempo, o acondicionamento do código EF pode manter seu código melhor organizado e seguindo a regra de separação de preocupações. Para mim, essa pode ser a única vantagem real do repositório e da unidade de trabalho, mas você precisa entender que seguir esta regra com a EF talvez torne seu código mais fácil de manter e de melhor legibilidade, mas no esforço inicial para criar seu aplicativo será muito maior e para aplicativos menores, isso pode ser uma complexidade desnecessária.

O segundo motivo está parcialmente correto. A grande desvantagem do EF é a arquitetura rígida, que dificilmente pode ser ridicularizada; portanto, se você deseja testar a camada superior da unidade, é necessário agrupar o EF de alguma forma para permitir que sua implementação seja ridicularizada. Mas isso tem muitas outras consequências que descrevi aqui .

Eu sigo o blog de Ayende . Se você já usou o NHibernate, provavelmente conhece os artigos dele. Esse cara recentemente escreveu vários artigos contra o uso de repositório com o NHibernate, mas o NHibernate é muito melhor ridicularizável.

Ladislav Mrnka
fonte
3
Você pode simular, IDbSettambém pode definir a interface personalizada no seu contexto derivado, mas isso é tudo. Depois que seu código usar o ChangeTracker, Entries ou o que mais for necessário, será necessário um grande esforço para agrupar todos eles.
Ladislav Mrnka
1
Sim, a EF não é uma ferramenta muito orientada para o desempenho. Pelo menos a MS tem muitas oportunidades para melhorar isso em versões futuras.
Ladislav Mrnka
2
@chiccodoro: Certo. Mas uma vez que sua classe simulada expõe IQueryableou aceita Expression<>como parâmetro que é internamente colocado na consulta Linq-para-entidades, você está definindo a lógica fora do componente simulado com efeitos colaterais que não podem ser testados com testes de unidade.
Ladislav Mrnka
8
Se estou usando DbSet e BdContext diretamente na minha camada de negócios, tenho que fazer referência ao EntityFramework.dll lá, bem como no meu projeto DataLayer. Só isso me diz que precisa de algum tipo de embalagem.
Ingó Vals
2
downvote: incompleto - abstrair o EF atrás de uma interface de repositório pode fazer com que exatamente o mesmo código de cliente seja executado no SL e no WPF.
precisa saber é
21

Estou enfrentando os mesmos problemas, e a simulabilidade para testes de unidade das camadas EF é importante. Mas eu deparei com este ótimo artigo que explica como configurar o EF 4.1 DbContext para ser ridicularizável, certificando-se de que o DbContext derivado implementou uma interface genérica e expõe o IDbSet em vez do DbSet. Como estou usando uma abordagem do Database First, porque nosso banco de dados já existe, simplesmente modifiquei os modelos T4 usados ​​para gerar meu DbContext derivado para gerá-lo para retornar interfaces IDbSet e derivar da minha interface genérica. Dessa forma, tudo pode ser facilmente zombado e você não precisa implementar sua própria Unidade de Trabalho ou padrão de repositório. Basta escrever seu código de serviço para consumir sua interface genérica e, quando for testá-lo,

http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

Kendall Bennett
fonte
5

Uma razão para criar o repositório é para que você possa ocultar a implementação do DBSet e DbContext se decidir mudar do EntityFramework para outra coisa ou vice-versa.

Por exemplo, eu estava usando o NHibernate e agrupei todas as chamadas para essa estrutura nas minhas classes de repositório. Eles retornam IEnumerable para que seus get sejam "genéricos" e meus repositórios têm as operações CRUD padrão (atualização, exclusão, etc.). Há muito que mudei para o Entity Framework. Ao fazer isso, não precisei alterar nada nas minhas classes ViewModel ou mais além, porque elas apontavam para o meu repositório - eu só precisava alterar o interior do meu repositório. Isso tornou a vida muito mais fácil ao migrar.

(Eu usei o NHibernate porque estamos nos conectando ao ISeries e, na época, não havia implementações afetivas de custo usando EF com o ISeries. O único disponível era pagar US $ 12.000 à IBM pelo DB2Connect)

Leniel Maccaferri
fonte
"Quase" (sobre o assunto de ocultar o DBSet e o DbContext), você descobrirá que não precisa expor o EF a nenhum consumidor (por exemplo, se você usar o DI), mas precisa de uma interface que exponha as propriedades IDbSet <T> ou vá um passo além e digite todas as suas propriedades como IQueryable <T>, mas meu argumento é que você pode ocultar completamente sua dependência do DbSet e do DbContext. As operações de CRUD podem ser gravadas como métodos de extensão, você pode escrever vários métodos de extensão para diferentes repositórios. No entanto, você não estaria ocultando o uso do LINQ.
quer