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

190

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

    
por StringBuilder 12.12.2012 / 20:50
fonte

9 respostas

93

Não vejo nenhum motivo para o padrão Repositório não funcionar com o Entity Framework. O padrão de repositório é uma camada de abstração que você coloca na sua 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 Repositório funciona bem nesse cenário. Eu não acredito que o Entity Framework é suficiente abstração para esconder o que acontece nos bastidores.

Eu usei o padrão Repositório com o Entity Framework como meu método de camada de acesso a dados e ainda estou para enfrentar um problema.

Outra vantagem de abstrair o DbContext com um Repositório é testabilidade de unidade . Você pode ter sua interface IRepository para a qual tem 2 implementações, uma (o Repositório real) que usa DbContext para conversar com o banco de dados e a segunda, FakeRepository que pode retornar objetos na memória / dados escamoteados. Isso torna sua IRepository unit-testable, assim, outras partes do código que usa 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
  }    
}
    
por 12.12.2012 / 22:02
fonte
401

A única melhor razão 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 é seu UoW (Unidade de Trabalho) e cada 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. Nos velhos tempos de realmente escrever 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 sua 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 sobre o ORM, mas não algo tão complexo quanto o UoW / repostitory. Eu iria com um padrão de serviço, onde você constrói uma API que seu aplicativo pode usar sem saber ou se importar se os dados são provenientes de Entity Framework, NHibernate ou uma API da Web. Isso é muito mais simples, já que você simplesmente adiciona métodos à sua classe de serviço para retornar os dados de que seu aplicativo precisa. Se você estava escrevendo um aplicativo de tarefas, por exemplo, você pode ter uma chamada de serviço para devolver os itens vencidos nesta semana e que ainda não foram concluídos. Tudo o que seu aplicativo sabe é que, se quiser essa informação, chama esse método. Dentro desse método e em seu serviço em geral, você interage com o Entity Framework ou com qualquer outro que esteja usando. Então, se mais tarde você decidir trocar ORMs ou puxar as informações de uma API da Web, só precisará alterar o serviço e o resto do seu código ficará feliz, nada mais sábio.

Pode soar como um argumento em potencial para usar o padrão de repositório, mas a principal diferença aqui é que um serviço é uma camada mais fina e é voltado para retornar dados totalmente prontos, em vez de algo em que você continua pesquisando. como com um repositório.

    
por 03.12.2013 / 03:23
fonte
42

Aqui está um take de Ayende Rahien: Arquitetando no pit of doom: Os males da camada de abstração do repositório

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

No entanto, recuperar suas entidades por meio de métodos de recuperação de dados específicos da consulta significa que seus testes de unidade agora são essencialmente testes de caixa branca e você não tem escolha, pois é necessário saber com antecedência exatamente qual método de repositório a unidade está sendo testada. vai ligar para zombar. E você ainda não está realmente testando as consultas, a menos que você também escreva testes de integração.

Estes são problemas complexos que precisam de uma solução complexa. Você não pode consertar isso apenas fingindo que todas as suas entidades são tipos separados, sem relacionamentos entre eles, e atomizando cada um deles em seu próprio repositório. Bem, você pode , mas é uma droga.

Atualização: Eu tive algum sucesso usando o provedor Esforço para o Entity Framework. Esforço é um provedor de memória (código aberto) que permite que você use EF em testes exatamente da maneira que você usaria em um banco de dados real. Estou pensando em mudar todos os testes neste projeto que estou trabalhando para usar este provedor, uma vez que parece tornar as coisas muito mais fáceis. É a única solução que encontrei até agora que aborda todos os problemas sobre os quais eu estava falando mais cedo. A única coisa é que há um pequeno atraso ao iniciar meus testes enquanto cria 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. Existe um artigo Code Project que fala sobre o uso do Esforço (versus SQL CE) para testes.

    
por 31.12.2012 / 19:17
fonte
16

A razão pela qual você provavelmente faria isso é porque é um pouco redundante. O Entity Framework oferece várias vantagens funcionais e de codificação, e é por isso que você o usa. Se você pegar isso e colocá-lo em um padrão de repositório, você estará usando outras camadas de acesso a dados. / p>     

por 20.03.2013 / 18:05
fonte
14

Em teoria, acho que faz sentido encapsular a lógica de conexão do banco de dados para torná-lo mais facilmente reutilizável, mas como o link abaixo argumenta, nossos frameworks modernos essencialmente cuidam disso agora.

Reconsiderando o padrão de repositório

    
por 12.12.2012 / 21:32
fonte
6

Uma boa razão para usar o padrão de repositório é permitir a separação de sua lógica de negócios e / ou sua interface do usuário de System.Data.Entity. Existem inúmeras vantagens nisso, incluindo benefícios reais em testes unitários, permitindo que ele use Fakes ou Mocks.

    
por 20.06.2013 / 17:41
fonte
0

Tivemos problemas com instâncias DbContext do Entity Framework duplicadas, mas diferentes quando um contêiner IoC que new () up repositories por tipo (por exemplo, um UserRepository e uma instância GroupRepository que cada um chama seu próprio IDbSet de 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 supõem que objetos criados com um contexto serão anexados corretamente como coleções filho a um novo objeto em outro contexto, às vezes ele falha e às vezes não depende da velocidade dos commits.

    
por 04.02.2015 / 21:38
fonte
-1

Depois de testar o padrão de repositório em um projeto pequeno, aconselho vivamente a não usá-lo; não porque complica o seu sistema, e não porque os dados de zombaria são um pesadelo, mas porque o seu teste se torna inútil !!

Os dados de simulação permitem adicionar detalhes sem cabeçalhos, adicionar registros que violem as restrições do banco de dados e remover entidades que o banco de dados se recusaria a remover. No mundo real, uma única atualização pode afetar várias tabelas, registros, 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 em um banco de dados real oferece 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 seus procedimentos armazenados fazem a coisa certa com os dados, retornar 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 no procedimento armazenado e milhares desses cenários.

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

O repositório foi a melhor prática naqueles dias em que estávamos usando o XBase, o AdoX e o Ado.Net, mas com entidades !! (Repositório através do repositório)

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

    
por 15.10.2015 / 04:40
fonte
-3

É devido a Migrações: Não é possível fazer migrações funcionarem, pois a cadeia de conexão reside no web.config. Mas, o DbContext reside na camada Repository. IDbContextFactory precisa ter uma string de configuração no banco de dados. Mas não há como as migrações obterem a string de conexão do web.config.

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

    
por 18.12.2015 / 06:16
fonte