Qual é o propósito de AsQueryable ()?

107

O objetivo é AsQueryable()apenas para que você possa passar um IEnumerablepara métodos que você pode esperar IQueryable, ou há uma razão útil para representar IEnumerablecomo IQueryable? Por exemplo, deve ser para casos como este:

IEnumerable<Order> orders = orderRepo.GetAll();

// I don't want to create another method that works on IEnumerable,
// so I convert it here.
CountOrders(orders.AsQueryable());

public static int CountOrders(IQueryable<Order> ordersQuery)
{
    return ordersQuery.Count();
}

Ou realmente faz algo diferente:

IEnumerable<Order> orders = orderRepo.GetAll();
IQueryable<Order> ordersQuery = orders.AsQueryable();

IEnumerable<Order> filteredOrders = orders.Where(o => o.CustomerId == 3);
IQueryable<Order> filteredOrdersQuery = ordersQuery.Where(o => o.CustomerId == 3);

// Are these executed in a different way?
int result1 = filteredOrders.Count();
int result2 = filteredOrdersQuery.Count();

As IQueryableversões desses métodos de extensão apenas criam uma Expressão que acaba fazendo a mesma coisa depois de executada? Minha pergunta principal é: qual é um caso de uso real para usar AsQueryable?

Ocelot20
fonte

Respostas:

65

Existem alguns usos principais.

  1. Conforme mencionado em outras respostas, você pode usá-lo para simular uma fonte de dados consultável usando uma fonte de dados na memória para que possa testar mais facilmente os métodos que eventualmente serão usados ​​em uma base não enumerável IQueryable.

  2. Você pode escrever métodos auxiliares para manipular coleções que podem ser aplicadas a sequências na memória ou a fontes de dados externas. Se você escrever seus métodos de ajuda para serem usados IQueryableinteiramente, você pode apenas usar AsQueryableem todos os enumeráveis ​​para usá-los. Isso permite que você evite escrever duas versões separadas de métodos auxiliares muito generalizados.

  3. Ele permite que você altere o tipo de tempo de compilação de um questionável para ser um IQueryable, em vez de algum tipo mais derivado. Com efeito; você o usaria em um IQueryableao mesmo tempo que usaria AsEnumerableem um IEnumerable. Você pode ter um objeto que implementa, IQueryablemas que também possui um Selectmétodo de instância . Se fosse esse o caso e você quisesse usar o Selectmétodo LINQ , seria necessário alterar o tipo de tempo de compilação do objeto para IQueryable. Você poderia apenas lançá-lo, mas por ter um AsQueryablemétodo, você pode tirar vantagem da inferência de tipo. Isso é simplesmente mais conveniente se a lista de argumentos genéricos for complexa e é realmente necessário se qualquer um dos argumentos genéricos for de tipos anônimos.

Servy
fonte
Obrigado pelo avivamento. Marcando isso como a resposta por ser a mais completa.
Ocelot20
5
Dado que IQueryable<T>deriva de, IEnumerable<T>você pode explicar o que quer dizer com "IQueryable com base não enumerável"?
Dai
2
@Dai Refere-se a um IQueryableque é mais do que apenas embrulhado IEnumerablee que realmente aproveita os recursos adicionais de um IQueryable.
Servy
# 1 é exatamente o que eu estava procurando. Obrigado por verificar.
one.beat.consumer
12

O caso mais válido que tenho para AsQueryable é o teste de unidade. Digamos que eu tenha o seguinte exemplo um tanto artificial

public interface IWidgetRepository
{
   IQueryable<Widget> Retrieve();
} 

public class WidgetController
{
   public IWidgetRepository WidgetRepository {get; set;}


   public IQueryable<Widget> Get()
   {
      return WidgetRepository.Retrieve();
   }
}

e quero escrever um teste de unidade para ter certeza de que o controlador devolve os resultados retornados do repositório. Seria mais ou menos assim:

[TestMethod]
public void VerifyRepositoryOutputIsReturned()
{    
    var widget1 = new Widget();
    var widget2 = new Widget();
    var listOfWidgets = new List<Widget>() {widget1, widget2};
    var widgetRepository = new Mock<IWidgetRepository>();
    widgetRepository.Setup(r => r.Retrieve())
      .Returns(listOfWidgets.AsQueryable());
    var controller = new WidgetController();
    controller.WidgetRepository = widgetRepository.Object;

    var results = controller.Get();

    Assert.AreEqual(2, results.Count());
    Assert.IsTrue(results.Contains(widget1));
    Assert.IsTrue(results.Contains(widget2));
}

onde, na verdade, tudo o que o método AsQueryable () me permite fazer é satisfazer o compilador ao configurar um mock.

Eu estaria interessado em onde isso é usado no código do aplicativo.

chris
fonte
11

Como sanjuro observou, a finalidade de AsQueryable () é explicada em Usando AsQueryable com Linq para objetos e Linq para SQL . Em particular, o artigo afirma,

Isso oferece excelentes benefícios em cenários de palavras reais onde você tem certos métodos em uma entidade que retorna um IQueryable de T e alguns métodos retornam List. Mas então você tem um filtro de regra de negócios que precisa ser aplicado em toda a coleção, independentemente se a coleção é retornada como IQueryable de T ou IEnumerable de T. Do ponto de vista de desempenho, você realmente deseja alavancar a execução do filtro de negócios no banco de dados se a coleção implementa IQueryable, caso contrário, recorrerá para aplicar o filtro de negócios na memória usando Linq para implementar a implementação de delegados.

Scott
fonte
6

A finalidade de AsQueryable () é amplamente explicada neste artigo Usando AsQueryable com Linq To Objects e Linq To SQL

Na seção de comentários do método Queryable.AsQueryable do MSDN:

Se o tipo de fonte implementa IQueryable, AsQueryable (IEnumerable) o retorna diretamente. Caso contrário, ele retorna um IQueryable que executa consultas chamando os métodos do operador de consulta equivalentes em Enumerable em vez daqueles em Queryable.

Isso é exatamente o que é mencionado e usado no artigo acima. Em seu exemplo, depende do que está retornando orderRepo.GetAll, IEnumerable ou IQueryable (Linq para Sql). Se retornar IQueryable, o método Count () será executado no banco de dados, caso contrário será executado na memória. Observe atentamente o exemplo no artigo referenciado.

sanjuro
fonte
3

IQueryableDocumentação de cotação da interface :

A interface IQueryable se destina à implementação por provedores de consulta.

Portanto, para alguém que pretende tornar sua estrutura de dados consultável em .NET, aquela estrutura de dados desnecessária pode ser enumerada ou ter um enumerador válido .

IEnumeratoré uma interface para iterar e processar o fluxo de dados .

Tigran
fonte
Você se importaria de reformular isso: "aquela estrutura de dados que não é necessária pode ser enumerada ou ter enumerador válido". Eu entendo a diferença entre IQueryablee IEnumerable, mas não entendo o caso de uso do AsQueryablemétodo de extensão. Estou ficando um pouco confuso com essa frase, porém, suspeito que possa ter a resposta que estou procurando (com base nos votos positivos).
Ocelot20
@ Ocelot20: isso significa que a estrutura de dados de chapéu apresentada pelo provedor que implementa IQueryable pode não fornecer uma capacidade de enumeração. Depende do provedor. Citando doc: "Consultas que não retornam resultados enumeráveis ​​são executadas quando o método Execute é chamado."
Tigran
Talvez esteja faltando algo óbvio, mas ainda não vejo a resposta para "por que eu precisaria usar AsQueryable()?" Se estou começando com um IEnumerable (algo já capaz de fornecer uma capacidade de enumeração), por que precisaria converter para IQueryableusar o método de extensão AsQueryable()? Talvez um pequeno exemplo de código para ilustrar onde isso seria útil? Parece que está faltando algo em sua explicação. Obrigado pelo seu tempo.
Ocelot20
@ Ocelot20: para realmente mostrar a diferença, preciso escrever um provedor de consultas. Você sabe que nós temos linq to sql, linq to xmle então .. se você quiser escrever um linqdriver que vise suas estruturas de dados ( linq to your data), para que os usuários de sua api tenham a possibilidade de executar linqconsultas em suas estruturas de dados, você deve escolherIQueryable
Tigran
2
Você parece estar explicando a diferença entre IQueryablee IEnumerable. Eu sei a diferença, e não é isso que estou pedindo. Estou perguntando por que alguém iria querer pegar algo que implementa IEnumerablee convertê-lo em um método de extensão IQueryableusando AsQueryable. É isso que você está respondendo? Ainda não sei dizer ..
Ocelot20