Por que não devo usar o padrão de repositório com o Entity Framework?


203

Durante uma entrevista de emprego, fui convidado a explicar por que o padrão do repositório não é um bom padrão para trabalhar com ORMs como o Entity Framework. Por que esse é o caso?


60
era uma pergunta capciosa
Omu

2
Provavelmente, eu teria respondido ao entrevistador que a Microsoft usa o padrão de repositório com muita frequência enquanto eles demonstram a estrutura da entidade: | .
Laurent Bourgault-Roy

1
Então, qual foi o motivo do entrevistador não ter sido uma boa ideia?
Bob Horn

3
O fato engraçado é que a pesquisa de "padrão de repositório" no Google fornece os resultados relacionados principalmente ao Entity Framework e como usar o padrão com o EF.
Arseni Mourzenko

2
verifique o blog de ayende ayende.com/blog . Base de dados sobre o que eu sei, ele costumava usar Pattern Repository mas eventualmente desistiu em favor do padrão de consulta de objeto
Jaime Sangcap

Respostas:


99

Não vejo nenhuma razão para o padrão de repositório não funcionar com o Entity Framework. O padrão do repositório é uma camada de abstração que você coloca na camada de acesso a dados. Sua camada de acesso a dados pode ser qualquer coisa, desde procedimentos armazenados puros do ADO.NET até o Entity Framework ou um arquivo XML.

Em sistemas grandes, onde você tem dados provenientes de diferentes fontes (banco de dados / XML / serviço da web), é bom ter uma camada de abstração. O padrão de repositório funciona bem nesse cenário. Não acredito que o Entity Framework seja abstração suficiente para ocultar o que acontece nos bastidores.

Eu usei o padrão de repositório com o Entity Framework como método da camada de acesso a dados e ainda estou enfrentando um problema.

Outra vantagem de abstrair o DbContextcom um Repositório é a testabilidade por unidade . Você pode ter sua IRepositoryinterface com duas implementações, uma (o Repositório real) que usa DbContextpara conversar com o banco de dados e a segunda, FakeRepositoryque pode retornar objetos na memória / dados simulados. Isso torna sua IRepositoryunidade testável, portanto, outras partes do código que são usadas IRepository.

public interface IRepository
{
  IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
  private YourDbContext db;
  private EFRepository()
  {
    db = new YourDbContext();
  }
  public IEnumerable<CustomerDto> GetCustomers()
  {
    return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
  }
}
public MockRepository : IRepository
{
  public IEnumerable<CustomerDto> GetCustomers()
  {
    // to do : return a mock list of Customers
    // Or you may even use a mocking framework like Moq
  }
}

Agora, usando o DI, você obtém a implementação

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}

3
Eu não disse que isso não funcionaria, eu também trabalhei com padrão de repositório na EF, mas hoje me perguntaram por que NÃO É BOM usar o padrão com o DataBase, aplicativo que usa o Database

2
Ok, já que esta é a resposta mais popular, eu a escolherei como Resposta correta #

65
Quando foi a última vez que o mais popular == correto?
HDave

14
DbContext já é um repositório, o repositório deve ser uma abstração de baixo nível. Se você deseja abstrair diferentes fontes de dados, crie objetos para representá-los.
Daniel Little

7
ColacX. Tentamos exatamente isso - o DBcontext diretamente na camada do controlador - e estamos voltando ao padrão de repo. Com o padrão Repo, os testes de unidade passaram de zombaria maciça do DbContext que falhou constantemente. A EF era difícil de usar e frágil e custou horas de pesquisa para nuances da EF. Agora temos pequenas zombarias simples do repositório. O código é mais limpo. A separação do trabalho é mais clara. Não concordo mais com a multidão que a EF já é um padrão de repo e já é testável por unidade.
Rhyous 11/07

435

O melhor motivo para não usar o padrão de repositório com o Entity Framework? O Entity Framework implementa um padrão de repositório. DbContexté sua UoW (unidade de trabalho) e cada um DbSeté o repositório. A implementação de outra camada além disso não é apenas redundante, mas dificulta a manutenção.

As pessoas seguem padrões sem perceber o propósito do padrão. No caso do padrão de repositório, o objetivo é abstrair a lógica de consulta de banco de dados de baixo nível. Antigamente, na verdade, escrevendo instruções SQL em seu código, o padrão de repositório era uma maneira de mover esse SQL de métodos individuais espalhados por toda a base de código e localizá-lo em um só lugar. Ter um ORM como Entity Framework, NHibernate etc. é um substituto para essa abstração de código e, como tal, nega a necessidade do padrão.

No entanto, não é uma má idéia criar uma abstração em cima do seu ORM, apenas nada tão complexo quanto UoW / repostitory. Eu usaria um padrão de serviço, no qual você constrói uma API que seu aplicativo pode usar sem saber ou se importar se os dados são provenientes do Entity Framework, NHibernate ou uma API da Web. Isso é muito mais simples, pois você simplesmente adiciona métodos à sua classe de serviço para retornar os dados que seu aplicativo precisa. Se você estava escrevendo um aplicativo de Tarefas pendentes, por exemplo, pode ter uma chamada de serviço para devolver itens que vencem esta semana e que ainda não foram concluídos. Tudo que seu aplicativo sabe é que, se quiser essas informações, ele chamará esse método. Dentro desse método e em seu serviço em geral, você interage com o Entity Framework ou qualquer outra coisa que esteja usando. Em seguida, se você decidir alternar ORMs ou extrair as informações de uma API da Web,

Pode parecer que esse é um argumento potencial para o uso do padrão de repositório, mas a principal diferença aqui é que um serviço é uma camada mais fina e é voltado para o retorno de dados completos, em vez de algo que você continua consultando, como em um repositório.


68
Essa parece ser a única resposta correta.
Mike Chamberlain

10
Você pode simular DbContextno EF6 + (consulte: msdn.microsoft.com/en-us/data/dn314429.aspx ). Mesmo em versões inferiores, você pode usar uma DbContextclasse do tipo falso com DbSets zombados , pois DbSetimplementa uma iterface IDbSet,.
Chris Pratt

14
@TheZenker, você pode não estar seguindo exatamente o padrão do repositório. A diferença mais estrita é o valor de retorno. Repositórios retornam consultas, enquanto serviços devem retornar enumeráveis. Mesmo isso não é tão preto e branco, pois há alguma sobreposição lá. É mais sobre como você o usa. Um repositório deve apenas retornar o conjunto de todos os objetos, nos quais você ainda consulta mais, enquanto o serviço deve retornar o conjunto de dados final e não deve oferecer suporte a consultas adicionais.
Chris Pratt

10
Correndo o risco de parecer egoísta: eles estão errados. Agora, no que diz respeito aos tutoriais oficiais, a Microsoft desistiu de usar repositórios do que vi desde o EF6. Em relação ao livro, não sei explicar por que o autor optou por usar repositórios. O que eu posso falar, como alguém nas trincheiras que constrói aplicativos de grande escala, é que o uso do padrão de repositório com o Entity Framework é um pesadelo de manutenção. Depois que você se move para algo mais complexo do que um punhado de repositórios, você acaba gastando uma quantidade exorbitante de tempo gerenciando seus repositórios / unidade de trabalho.
Chris Pratt

6
Normalmente, tenho apenas um serviço por banco de dados ou método de acesso. Eu uso métodos genéricos para consultar vários tipos de entidade do mesmo conjunto de métodos. Eu uso o Ninject para injetar meu contexto no meu serviço e, em seguida, meu serviço nos meus controladores, para que tudo fique limpo e arrumado.
22815 Chris Pratt

45

Aqui está um exemplo de Ayende Rahien: Arquitetando no poço da destruição: Os males da camada de abstração do repositório

Ainda não tenho certeza se concordo com a conclusão dele. É um problema - por um lado, se eu envolver meu EF Context em repositórios específicos de tipo com métodos de recuperação de dados específicos de consulta, na verdade, posso testar meu código (mais ou menos), o que é quase impossível com Entity Quadro sozinho. Por outro lado, perco a capacidade de fazer consultas avançadas e manutenção semântica de relacionamentos (mas mesmo quando tenho acesso total a esses recursos, sempre sinto que estou andando em cascas de ovos pela EF ou por qualquer outra ORM que eu possa escolher , como eu nunca sei quais métodos sua implementação IQueryable pode ou não oferecer suporte, se ele interpretará minha inclusão em uma coleção de propriedades de navegação como uma criação ou apenas uma associação, se ela será carregada preguiçosamente ou ansiosamente ou não será carregada padrão etc. então talvez isso seja para melhor. O "mapeamento" relacional a objetos de impedância zero é algo de criatura mitológica - talvez seja por isso que a última versão do Entity Framework tenha o codinome "Magic Unicorn").

No entanto, recuperar suas entidades por meio de métodos de recuperação de dados específicos de consulta significa que seus testes de unidade agora são essencialmente testes de caixa branca e você não tem escolha nesse assunto, pois você deve saber com antecedência exatamente qual método de repositório a unidade em teste irá usar. ligue para zombar dele. E você ainda não está testando as consultas em si, a menos que você também escreva testes de integração.

Esses são problemas complexos que precisam de uma solução complexa. Você não pode corrigi-lo apenas fingindo que todas as suas entidades são tipos separados sem relacionamentos entre elas e atomizá-las cada uma em seu próprio repositório. Bem, você pode , mas é uma merda.

Atualização: obtive algum sucesso ao usar o provedor de esforço para o Entity Framework. O Effort é um provedor de memória (código aberto) que permite usar o EF em testes exatamente da maneira que você usaria em um banco de dados real. Estou pensando em mudar todos os testes deste projeto em que estou trabalhando para usar esse provedor, pois parece facilitar muito as coisas. É a única solução que eu encontrei até agora que aborda todos os problemas que eu estava reclamando anteriormente. A única coisa é que há um pequeno atraso ao iniciar meus testes, pois ele está criando o banco de dados na memória (ele usa outro pacote chamado NMemory para fazer isso), mas não vejo isso como um problema real. Há um artigo do Code Project que fala sobre o uso do esforço (versus SQL CE) para teste.


3
Qualquer artigo de arquitetura sem mencionar o teste de unidade é enviado automaticamente para a lixeira para mim. Um dos pontos do padrão de repositório é obter alguma capacidade de teste.
Sleeper Smith

3
Você ainda pode fazer testes de unidade sem agrupar o contexto EF (que já é um repositório). Você deve testar sua unidade / domínio, não as consultas ao banco de dados (são testes de integração).
Daniel Little

2
A testabilidade da EF melhorou bastante na versão 6. Agora você pode zombar totalmente DbContext. Independentemente disso, você sempre pode zombar DbSet, e essa é a carne do Entity Framework, de qualquer maneira. DbContexté pouco mais do que uma classe para abrigar suas DbSetpropriedades (repositórios) em um local (unidade de trabalho), especialmente em um contexto de teste de unidade, onde todo o material de inicialização e conexão do banco de dados não é desejado ou necessário.
Chris Pratt

Perder a navegação da entidade relacionada é ruim e é contra OOP, mas você terá mais controle sobre o que está sendo consultado.
Alireza

A ponto de testar, o EF Core percorreu um longo caminho com os provedores In-Memory e In-Memory with Sqlite prontos para permitir testes de unidade. Trazer docker quando você precisar de testes de integração para executar testes em um banco de dados em contêiner.
Sudhanshu Mishra

16

A razão pela qual você provavelmente faria isso é porque é um pouco redundante. O Entity Framework oferece diversas vantagens funcionais de codificação e é por isso que você o usa. Se você o pega e o embrulha em um padrão de repositório, você está descartando essas vantagens, assim como qualquer outra camada de acesso a dados.


você poderia dizer algumas das vantagens de "O Entity Framework oferece uma grande variedade de vantagens funcionais e de codificação"?
ManirajSS

2
foi isso que ele quis dizer. var id = Entity.Where (i => i.Id == 1337). Single () encapsulado e agrupado em um repositório, basicamente você não pode fazer lógica de consulta como essa de fora, o que o força a adicionar mais código a o repositório e a interface para buscar o ID. B devolver o contexto entidade a partir do repositório de modo que você pode escrever a lógica de consulta (que é apenas um disparate)
ColacX

14

Em teoria, acho que faz sentido encapsular a lógica de conexão com o banco de dados para torná-la mais facilmente reutilizável, mas, como o link abaixo argumenta, nossas estruturas modernas essencialmente cuidam disso agora.

Reconsiderando o Padrão de Repositório


Eu gostei do artigo, mas IMHO para aplicativos corporativos, a camada de abstração entre DAL e Bl DEVE ter o recurso, pois você não sabia o que exatamente será usado amanhã. Mas obrigado por compartilhar o link

1
Embora pessoalmente eu ache que é verdade, por exemplo, para o NHibernate ( ISessionFactorye ISessioné facilmente ridicularizável) DbContext, infelizmente não é tão fácil assim com ... #
Patryk iewiek #

6

Um bom motivo para usar o padrão de repositório é permitir a separação da lógica de negócios e / ou da interface do usuário do System.Data.Entity. Existem inúmeras vantagens nisso, incluindo benefícios reais em testes de unidade, permitindo o uso de falsificações ou zombarias.


Eu concordo com esta resposta. Meus repositórios são basicamente métodos de extensão, que não fazem nada além de construir árvores de expressão. Sobre uma abstração MUITO simples que simplesmente fornece funcionalidade genérica diretamente na parte superior do dbcontext. O único objetivo real da abstração é facilitar a IoC. Acho que as pessoas tentam fazer coisas em seus repositórios que não deveriam fazer. Eles realizam repo por entidade ou colocam a lógica de negócios que deveria estar na camada de serviços. Você realmente só precisa de um repositório genérico simples. Não é necessário, apenas fornece uma interface consistente.
Brandon

Só mais uma coisa que eu queria acrescentar. Sim, o CQRS é uma metodologia muito superior na maioria dos casos. Para alguns clientes em que trabalhei quando o pessoal do banco de dados não trabalha bem com os desenvolvedores (o que acontece com mais frequência do que se imagina, especialmente nos bancos), o EF sobre SQL é a melhor opção. Nesse cenário específico, quando você não tem absolutamente nenhum controle sobre seu banco de dados, o padrão do repositório faz sentido. Porque se parece muito com a estrutura de dados e é fácil traduzir o que está acontecendo no banco de dados e vice-versa. É realmente uma decisão política e logística na minha opinião. Para apaziguar os deuses do DB.
Brandon

1
Na verdade, estou começando a questionar minhas opiniões anteriores sobre isso. EF é um padrão combinado de Unidade de Trabalho e Repositório. Como Chris Pratt mencionou acima com o EF6, você pode facilmente zombar dos objetos Context e DbSet. Eu ainda acredito que o acesso a dados deve ser agrupado em classes para proteger as classes de lógica de negócios do mecanismo real de acesso a dados, mas fazer o trabalho todo e envolver o EF com outro repositório e a abstração da Unidade de Trabalho parece ser um exagero.
James Culshaw

Não acho que essa seja uma boa resposta, porque sua declaração de suporte é apenas a de que existem inúmeras vantagens ao listar apenas uma. teste de unidade.
Joel McBeth

@jcmcbeth se você olhar para o meu comentário diretamente acima do seu, verá que mudei minha opinião original com relação ao padrão de repositório e à EF.
precisa

0

Tivemos problemas com instâncias duplicadas, mas diferentes, do Entity Framework DbContext, quando um contêiner de IoC com novos repositórios up () por tipo (por exemplo, uma instância de UserRepository e GroupRepository que cada um chama seu próprio IDbSet da DBContext), às vezes pode causar vários contextos por solicitação (em um contexto MVC / web).

Na maioria das vezes, ainda funciona, mas quando você adiciona uma camada de serviço e esses serviços assumem que os objetos criados com um contexto serão corretamente anexados como coleções filho a um novo objeto em outro contexto, às vezes falha e às vezes não ' t dependendo da velocidade das confirmações.


Eu encontrei esse problema em vários projetos diferentes.
ColacX 21/01

0

Depois de experimentar o padrão de repositório em um projeto pequeno, aconselho vivamente a não usá-lo; não porque complica seu sistema, e não porque zombar de dados é um pesadelo, mas porque seus testes se tornam inúteis !!

A zombaria de dados permite adicionar detalhes sem cabeçalhos, adicionar registros que violam as restrições do banco de dados e remover entidades que o banco de dados recusaria remover. No mundo real, uma única atualização pode afetar várias tabelas, logs, histórico, resumos etc., bem como colunas como o campo da data da última modificação, chaves geradas automaticamente, campos computados.

Em resumo, seu teste no banco de dados real fornece resultados reais e você pode testar não apenas seus serviços e interfaces, mas também o comportamento do banco de dados. Você pode verificar se os procedimentos armazenados fazem a coisa certa com os dados, retornam o resultado esperado ou se o registro que você enviou para excluir realmente foi excluído! Esses testes também podem expor problemas como esquecer de gerar erros do procedimento armazenado e milhares desses cenários.

Acho que a estrutura da entidade implementa o padrão de repositório melhor do que qualquer um dos artigos que li até agora e vai muito além do que eles estão tentando realizar.

O repositório era uma prática recomendada nos dias em que estávamos usando XBase, AdoX e Ado.Net, mas com entidade !! (Repositório sobre repositório)

Por fim, acho que muitas pessoas investem muito tempo aprendendo e implementando o padrão de repositório e se recusam a deixá-lo ir. Principalmente para provar a si mesmos que não perderam tempo.


1
Exceto que você NÃO deseja testar o comportamento do banco de dados em testes de unidade, pois esse nível não é totalmente esse.
Mariusz Jamro

Sim, o que você está falando aqui é sobre testes de integração e é realmente valioso, mas os testes de unidade são totalmente diferentes. Seus testes de unidade nunca devem atingir um banco de dados real, mas recomendamos que você adicione testes de integração.
Chris Pratt

-3

Isso ocorre devido às migrações: não é possível fazer as migrações funcionarem, pois a cadeia de conexão reside no web.config. Mas, o DbContext reside na camada Repositório. O IDbContextFactory precisa ter uma cadeia de configuração no banco de dados. Mas não há como as migrações obterem a cadeia de conexão do web.config.

Existem soluções, mas ainda não encontrei uma solução limpa para isso!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.