Qual è lo scopo di AsQueryable ()?


107

Lo scopo è AsQueryable()solo così puoi passare IEnumerablea metodi che potrebbero aspettarti IQueryable, o c'è una ragione utile per rappresentare IEnumerablecome IQueryable? Ad esempio, dovrebbe essere per casi come questo:

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();
}

O effettivamente gli fa fare qualcosa di diverso:

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();

Le IQueryableversioni di questi metodi di estensione creano solo un'espressione che finisce per fare la stessa cosa una volta eseguita? La mia domanda principale è: qual è un vero caso d'uso per l'utilizzo AsQueryable?

Risposte:


65

Ci sono alcuni usi principali.

  1. Come accennato in altre risposte, è possibile utilizzarlo per simulare un'origine dati interrogabile utilizzando un'origine dati in memoria in modo da poter testare più facilmente metodi che verranno eventualmente utilizzati su una base non enumerabile IQueryable.

  2. È possibile scrivere metodi di supporto per la manipolazione di raccolte che possono essere applicate a sequenze in memoria o origini dati esterne. Se scrivi i tuoi metodi di aiuto per usarli IQueryableinteramente, puoi semplicemente usarli AsQueryablesu tutti gli enumerabili per usarli. Ciò consente di evitare di scrivere due versioni separate di metodi di supporto molto generalizzati.

  3. Consente di modificare il tipo di tempo di compilazione di un oggetto interrogabile in un tipo IQueryable, piuttosto che in un tipo più derivato. In effetti; lo useresti su un IQueryableallo stesso tempo che useresti AsEnumerablesu un IEnumerable. Potresti avere un oggetto che implementa IQueryablema che ha anche un Selectmetodo di istanza . In tal caso e si desidera utilizzare il Selectmetodo LINQ , è necessario modificare il tipo di tempo di compilazione dell'oggetto in IQueryable. Potresti semplicemente lanciarlo, ma disponendo di un AsQueryablemetodo puoi sfruttare l'inferenza del tipo. Questo è semplicemente più conveniente se l'elenco di argomenti generici è complesso, ed è effettivamente necessario se uno qualsiasi degli argomenti generici è di tipo anonimo.


Grazie per il risveglio. Contrassegnarlo come risposta poiché è il più completo.
Ocelot20

5
Dato che IQueryable<T>deriva da, IEnumerable<T>puoi spiegare cosa intendi per "IQueryable non enumerabile"?
Dai

2
@Dai Si riferisce a un file IQueryableche è più di un semplice wrapping IEnumerablee che in realtà sfrutta le funzionalità aggiuntive di un file IQueryable.
Servy

# 1 è esattamente quello che stavo cercando. Grazie per la verifica.
one.beat.consumer

12

Il caso più valido che ho per AsQueryable è il test di unità. Supponiamo che io abbia il seguente esempio un po 'artificioso

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

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


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

e voglio scrivere uno unit test per assicurarmi che il controller restituisca i risultati restituiti dal repository. Sarebbe simile a questo:

[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));
}

dove in realtà, tutto ciò che il metodo AsQueryable () mi permette di fare è soddisfare il compilatore quando si imposta un mock.

Sarei comunque interessato a dove viene utilizzato nel codice dell'applicazione.


11

Come ha notato sanjuro, lo scopo di AsQueryable () è spiegato in Uso di AsQueryable con Linq To Objects e Linq To SQL . In particolare, l'articolo afferma,

Ciò offre vantaggi eccellenti in scenari di parole reali in cui si hanno determinati metodi su un'entità che restituiscono un IQueryable di T e alcuni metodi restituiscono List. Ma poi hai il filtro delle regole di business che deve essere applicato a tutta la raccolta indipendentemente dal fatto che la raccolta venga restituita come IQueryable di T o IEnumerable di T. Dal punto di vista delle prestazioni, vuoi davvero sfruttare l'esecuzione del filtro aziendale sul database se la raccolta implementa IQueryable, altrimenti ricorrere all'applicazione del filtro aziendale in memoria utilizzando Linq all'implementazione dell'oggetto dei delegati.


6

Lo scopo di AsQueryable () è ampiamente spiegato in questo articolo Utilizzo di AsQueryable con Linq To Objects e Linq To SQL

Dalla sezione Osservazioni del metodo MSDN Queryable.AsQueryable:

Se il tipo di origine implementa IQueryable, AsQueryable (IEnumerable) lo restituisce direttamente. In caso contrario, restituisce un oggetto IQueryable che esegue le query chiamando i metodi dell'operatore di query equivalenti in Enumerable anziché quelli in Queryable.

Questo è esattamente ciò che viene menzionato e utilizzato nell'articolo precedente. Nel tuo esempio, dipende da cosa restituisce orderRepo.GetAll, IEnumerable o IQueryable (da Linq a Sql). Se restituisce IQueryable, il metodo Count () verrà eseguito sul database altrimenti verrà eseguito in memoria. Guarda attentamente l'esempio nell'articolo di riferimento.


3

IQueryableDocumentazione di citazione dell'interfaccia :

L'interfaccia IQueryable è destinata all'implementazione da parte dei provider di query.

Quindi, per qualcuno che intende rendere interrogabile la sua data struttura in .NET, quella struttura dati che non è necessaria può essere enumerata o avere un enumeratore valido .

IEnumeratorè invece un'interfaccia per l'iterazione e l'elaborazione di flussi di dati .


Ti dispiacerebbe riformulare questo: "quella struttura di dati che non è necessaria può essere enumerata o avere un enumeratore valido". Comprendo la differenza tra IQueryablee IEnumerable, ma non capisco il caso d'uso del AsQueryablemetodo di estensione. Tuttavia, mi sto un po 'inciampando su quella frase, che sospetto potrebbe avere la risposta che sto cercando (in base ai voti positivi).
Ocelot20

@ Ocelot20: ciò significa che la struttura dei dati presentata dal provider che implementa IQueryable potrebbe non fornire un limite di enumerazione. Dipende dal fornitore. Citando doc: "Le query che non restituiscono risultati enumerabili vengono eseguite quando viene chiamato il metodo Execute."
Tigran

Forse mi manca qualcosa di ovvio, ma ancora non vedo la risposta a "perché dovrei usarlo AsQueryable()?" Se sto iniziando con un IEnumerable (qualcosa già in grado di fornire una capacità di enumerazione), perché dovrei convertirlo IQueryableutilizzando il metodo di estensione AsQueryable()? Forse un piccolo esempio di codice per illustrare dove sarebbe utile? Mi sembra che manchi qualcosa nella tua spiegazione. Grazie per il tuo tempo.
Ocelot20

@ Ocelot20: per mostrare davvero la differenza ho bisogno di scrivere un provider di query. Sai che abbiamo linq to sql, linq to xmle quindi .. se vuoi scrivere un linqdriver che abbia come target le tue strutture di dati ( linq to your data), in modo che gli utenti della tua API abbiano la possibilità di eseguire linqquery sulle tue strutture di dati, devi scegliereIQueryable
Tigran

2
Sembra che tu stia spiegando la differenza tra IQueryablee IEnumerable. Conosco la differenza e non è quello che chiedo. Sto chiedendo perché qualcuno vorrebbe prendere qualcosa che implementa IEnumerable, e convertirlo in un IQueryableutilizzando il AsQueryablemetodo di estensione. È questo che stai rispondendo? Non riesco ancora a dirlo ..
Ocelot20
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.