Quel est le but d'AsQueryable ()?

107

Le but est-il AsQueryable()juste de pouvoir transmettre des IEnumerableméthodes auxquelles on pourrait s'attendre IQueryable, ou y a-t-il une raison utile de représenter IEnumerablecomme IQueryable? Par exemple, est-ce censé être pour des cas comme celui-ci:

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 est-ce que cela le fait réellement faire quelque chose de différent:

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

Les IQueryableversions de ces méthodes d'extension construisent-elles simplement une expression qui finit par faire la même chose une fois exécutée? Ma question principale est, quel est un cas d'utilisation réel pour l'utilisation AsQueryable?

Ocelot20
la source

Réponses:

65

Il existe quelques utilisations principales.

  1. Comme mentionné dans d'autres réponses, vous pouvez l'utiliser pour simuler une source de données interrogeable à l'aide d'une source de données en mémoire afin de pouvoir tester plus facilement des méthodes qui seront éventuellement utilisées sur une base non énumérable IQueryable.

  2. Vous pouvez écrire des méthodes d'assistance pour manipuler des collections qui peuvent s'appliquer à des séquences en mémoire ou à des sources de données externes. Si vous écrivez vos méthodes d'aide à utiliser IQueryableentièrement, vous pouvez simplement les utiliser AsQueryablesur tous les énumérables pour les utiliser. Cela vous permet d'éviter d'écrire deux versions distinctes de méthodes d'assistance très généralisées.

  3. Il vous permet de changer le type de compilation d'un objet interrogeable en un IQueryable, plutôt qu'en un type plus dérivé. En effet; vous l'utiliseriez sur un IQueryableaux mêmes moments que AsEnumerablesur un IEnumerable. Vous pouvez avoir un objet qui implémente IQueryablemais qui a également une Selectméthode d' instance . Si tel était le cas et que vous souhaitiez utiliser la Selectméthode LINQ , vous devrez modifier le type de compilation de l'objet en IQueryable. Vous pouvez simplement le lancer, mais en ayant une AsQueryableméthode, vous pouvez tirer parti de l'inférence de type. C'est simplement plus pratique si la liste d'arguments génériques est complexe, et c'est en fait nécessaire si l'un des arguments génériques est de type anonyme.

Servy
la source
Merci pour le renouveau. Marquer ceci comme la réponse car c'est la plus complète.
Ocelot20
5
Étant donné que cela IQueryable<T>dérive de, IEnumerable<T>pouvez-vous s'il vous plaît expliquer ce que vous entendez par «IQueryable basé non énumérable»?
Dai
2
@Dai Cela fait référence à un IQueryablequi est plus qu'un simple enveloppé IEnumerable, et qui exploite en fait les capacités supplémentaires d'un IQueryable.
Servy le
Le n ° 1 est exactement ce que je cherchais. Merci d'avoir vérifié.
one.beat.consumer
12

Le cas le plus valable que j'ai pour AsQueryable est le test unitaire. Disons que j'ai l'exemple quelque peu artificiel suivant

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

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


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

et je veux écrire un test unitaire pour m'assurer que le contrôleur renvoie les résultats renvoyés par le référentiel. Cela ressemblerait à quelque chose comme ceci:

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

où vraiment, tout ce que la méthode AsQueryable () me permet de faire est de satisfaire le compilateur lors de la configuration d'une maquette.

Je serais cependant intéressé de savoir où cela est utilisé dans le code d'application.

Chris
la source
11

Comme sanjuro l'a noté, le but d'AsQueryable () est expliqué dans Utilisation d'AsQueryable avec des objets Linq To et Linq To SQL . En particulier, l'article indique,

Cela offre d'excellents avantages dans les scénarios de mots réels où vous avez certaines méthodes sur une entité qui retournent un IQueryable de T et certaines méthodes retournent une liste. Mais alors vous avez un filtre de règle métier qui doit être appliqué sur toute la collection, que la collection soit retournée comme IQueryable de T ou IEnumerable de T.Du point de vue des performances, vous voulez vraiment tirer parti de l'exécution du filtre d'entreprise sur la base de données si la collection implémente IQueryable sinon revient à appliquer le filtre métier en mémoire à l'aide de Linq à l'implémentation d'objet des délégués.

Scott
la source
6

Le but d'AsQueryable () est largement expliqué dans cet article Utilisation d'AsQueryable avec des objets Linq To et Linq To SQL

À partir de la section Remarques de MSDN Queryable.AsQueryable, méthode:

Si le type de source implémente IQueryable, AsQueryable (IEnumerable) le renvoie directement. Sinon, il renvoie un IQueryable qui exécute des requêtes en appelant les méthodes d'opérateur de requête équivalentes dans Enumerable au lieu de celles de Queryable.

C'est exactement ce qui est mentionné et utilisé dans l'article ci-dessus. Dans votre exemple, cela dépend de ce que orderRepo.GetAll retourne, IEnumerable ou IQueryable (Linq to Sql). S'il retourne IQueryable, la méthode Count () sera exécutée sur la base de données sinon elle sera exécutée en mémoire. Regardez attentivement l'exemple dans l'article référencé.

Sanjuro
la source
3

Interface IQueryablecitant la documentation:

L'interface IQueryable est destinée à être mise en œuvre par les fournisseurs de requêtes.

Donc, pour quelqu'un qui a l'intention de rendre sa structure de données interrogeable dans .NET, cette structure de données qui n'est pas nécessaire peut être énumérée ou avoir un énumérateur valide .

IEnumeratorest une interface pour itérer et traiter le flux de données à la place.

Tigran
la source
Pourriez-vous reformuler ceci: "cette structure de données qui n'est pas nécessaire peut être énumérée ou avoir un énumérateur valide". Je comprends la différence entre IQueryableet IEnumerable, mais je ne comprends pas le cas d'utilisation de la AsQueryableméthode d'extension. Je suis cependant un peu trébuché sur cette phrase, qui, je suppose, pourrait avoir la réponse que je recherche (sur la base des votes positifs).
Ocelot20
@ Ocelot20: cela signifie que la structure de données présentée par le fournisseur qui implémente IQueryable peut ne pas fournir de capabilité d'énumération. C'est au fournisseur. Citant la documentation: "Les requêtes qui ne renvoient pas de résultats énumérables sont exécutées lorsque la méthode Execute est appelée."
Tigran
Il me manque peut-être quelque chose d'évident, mais je ne vois toujours pas la réponse à la question «pourquoi devrais-je utiliser AsQueryable()?» Si je commence avec un IEnumerable (quelque chose déjà capable de fournir une capacité d'énumération), pourquoi aurais-je besoin de convertir en IQueryableutilisant la méthode d'extension AsQueryable()? Peut-être un petit exemple de code pour illustrer où cela serait utile? Il me semble qu'il me manque quelque chose dans votre explication. Merci pour votre temps.
Ocelot20
@ Ocelot20: pour vraiment montrer la différence, j'ai besoin d'écrire un fournisseur de requêtes. Vous savez que nous avons linq to sql, linq to xmlet donc .. si vous voulez écrire un linqpilote qui cible vos strucutres de données ( linq to your data), afin que les utilisateurs de votre api aient la possibilité d'exécuter des linqrequêtes sur vos structures de données, vous devez choisirIQueryable
Tigran
2
Vous semblez expliquer la différence entre IQueryableet IEnumerable. Je connais la différence, et ce n'est pas ce que je demande. Je demande pourquoi quelqu'un voudrait prendre quelque chose qui implémente IEnumerableet le convertir en IQueryableutilisant la AsQueryableméthode d'extension. C'est ce à quoi vous répondez? Je ne peux toujours pas dire ..
Ocelot20