Retornando um IQueryable de um IRepository

8

Usando o padrão Repository, é adequado retornar um IQueryable de um conjunto de dados (tabela) para uso genérico?

É muito útil em muitos casos, especialmente ao usar bibliotecas externas que utilizam essa interface, por exemplo, alguns plugins que classificam / filtram e se ligam a elementos da interface do usuário.

No entanto, expor um IQueryable às vezes parece deixar o design propenso a erros. Isso, associado a um uso incorreto de carregamento lento, pode levar a graves ocorrências de desempenho.

Por outro lado, ter métodos de acesso para cada uso parece redundante e também muito trabalhoso (considerando testes de unidade, etc.).

Mihalis Bagos
fonte
2
OMI: Ocultar a tecnologia de persistência (EF, NHibernate) dos desenvolvedores é uma má ideia. Apenas exponha o que essa tecnologia expõe.
Euphoric
@Euphoric Ok, mas se no ASP.NET MVC eu tenho controladores com 3000 linhas onde as pessoas criam com alegria new DbContext()sempre que precisam e ligam SaveChanges()quando querem. Não é possível testar nada e a única coisa que consigo pensar é em uma camada adicional ocultando-a.
Mateusz
@Mateusz Isso não é problema da tecnologia, mas das pessoas. Você tem desenvolvedores de baixa qualidade ou gerenciamento de baixa qualidade, que é incapaz de treinar adequadamente esses desenvolvedores.
Eufórico

Respostas:

12

Mark Seemann tem um excelente post sobre este assunto: IQueryable is Tight Coupling . Ele resume bem na parte final (ênfase minha):

Você pode pensar que tudo isso é um exercício teórico, mas realmente importa. Ao escrever o Código Limpo, é importante projetar uma API de forma que fique claro o que ela faz.

Uma interface como essa oferece garantias falsas:

public interface IRepository
{
    IQueryable<T> Query<T>();
}

De acordo com a lei do LSP e Postel, parece garantir que você possa escrever qualquer expressão de consulta (não importa quão complexa) contra a instância retornada, e sempre funcionaria .

Na prática, isso nunca vai acontecer.

Os programadores que definem essas interfaces invariavelmente têm em mente um ORM específico e implicitamente tendem a permanecer dentro dos limites que sabem que são seguros para esse ORM específico. Esta é uma abstração com vazamento.

Se você tem um ORM específico em mente, seja explícito. Não esconda atrás de uma interface. Cria a ilusão de que você pode substituir uma implementação por outra. Na prática, isso é impossível. Imagine tentar fornecer uma implementação em um armazenamento de eventos.

O bolo é uma mentira.

O ORM, como o Entity Framework, é uma implementação do padrão Repositório e Unidade de Trabalho. Não há necessidade de envolvê-los em outro.

Kristof Claes
fonte
Obrigado pela resposta. Parece que minhas preocupações atendem às de Seemann muitas vezes, mas na verdade não tomei tempo para lê-lo, o que pretendo. A única coisa que discordo é que um repositório - mesmo se fortemente acoplado - é muito útil para encapsular a lógica de negócios, mesmo que em um nível de acesso mais simples (por exemplo: VisibleItems, DeletedItems) etc., e ajude com o conceito DRY.
Mihalis Bagos
@ Kristof Apenas uma pergunta para "Entity Framework são implementações do repositório e do padrão da Unidade de Trabalho". O que posso fazer para que as pessoas não coloquem new DbContext()controlador e lógica complexa no método único no MVC e também não coloquem lógica nas visualizações? Eu tenho código legado em que não posso fazer nenhum teste de unidade. Para habilitar os testes e impedir que as pessoas façam códigos muito ruins, preciso de uma camada adicional. Portanto, preciso implementar meus próprios repositórios para fins educacionais.
Mateusz
Existem outras opções, como classes de serviço ou classes de comando e consulta.
Kristof Claes
1

Não haverá consenso sobre este assunto. Na minha opinião e experiência, um Repositório deve retornar objetos com usos específicos. Pelo menos se você usar o Repository Pattern conforme definido por Eric Evens no DDD. Um repositório é uma "ponte" que conecta lógica de negócios, persistência e fábricas.

Se você deseja acessar a persistência mais diretamente, talvez esteja procurando o Padrão de Gateway.

No entanto, pelo que você diz aqui, você gostaria de ocultar a exposição à persistência para que o Padrão de Proxy seja útil para você.

Patkos Csaba
fonte
Embora a exposição à persistência não seja uma preocupação principal, é obrigatória pelo menos uma abstração de nível superior para padrões como entidades pai-filho. Também examinarei esses padrões, obrigado!
Mihalis Bagos