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?
asp.net-mvc
entity-framework
unit-of-work
StringBuilder
fonte
fonte
Respostas:
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
DbContext
com um Repositório é a testabilidade por unidade . Você pode ter suaIRepository
interface com duas implementações, uma (o Repositório real) que usaDbContext
para conversar com o banco de dados e a segunda,FakeRepository
que pode retornar objetos na memória / dados simulados. Isso torna suaIRepository
unidade testável, portanto, outras partes do código que são usadasIRepository
.Agora, usando o DI, você obtém a implementação
fonte
O melhor motivo para não usar o padrão de repositório com o Entity Framework? O Entity Framework já implementa um padrão de repositório.
DbContext
é sua UoW (unidade de trabalho) e cada umDbSet
é 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.
fonte
DbContext
no EF6 + (consulte: msdn.microsoft.com/en-us/data/dn314429.aspx ). Mesmo em versões inferiores, você pode usar umaDbContext
classe do tipo falso comDbSet
s zombados , poisDbSet
implementa uma iterfaceIDbSet
,.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.
fonte
DbContext
. Independentemente disso, você sempre pode zombarDbSet
, e essa é a carne do Entity Framework, de qualquer maneira.DbContext
é pouco mais do que uma classe para abrigar suasDbSet
propriedades (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.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.
fonte
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
fonte
ISessionFactory
eISession
é facilmente ridicularizável)DbContext
, infelizmente não é tão fácil assim com ... #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.
fonte
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.
fonte
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.
fonte
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!
fonte