web-dev-qa-db-fra.com

Pourquoi utiliser .AsEnumerable () plutôt que de transtyper en IEnumerable <T>?

L'une des méthodes d'extension sur IEnumerable<T> Est .AsEnumerable(). Cette méthode convertit l'objet énumérable auquel il a été appelé en une instance de IEnumerable<T>. Cependant, puisqu'un objet doit implémenter IEnumerable<T> Pour s'appliquer à cette méthode d'extension, la conversion en IEnumerable<T> Est une simple question de transtypage en IEnumerable<T>. Ma question est pourquoi cette méthode existe-t-elle?

Exemple:

List<string> strings = new List<string>() { "test", "test2", "test3" };
IEnumerable<string> stringsEnum1 = strings.AsEnumerable();
IEnumerable<string> stringsEnum2 = (IEnumerable<string>)strings;

Dans l'exemple ci-dessus, stringsEnum1 Et stringsEnum2 Sont équivalents. Quel est l'intérêt de la méthode d'extension?

Edit: En corollaire, pourquoi existe-t-il une méthode .AsQueryable() lors de la conversion en IQueryable<T> Est équivalente?

62
Erik Forbes

La lisibilité est le principal problème ici. Considérez que

Table.AsEnumerable().Where(somePredicate)

est beaucoup plus lisible que

((IEnumerable<TableObject>)Table).Where(somePredicate).

Ou imaginez vouloir exécuter une partie de la requête sur SQL Server et le reste en mémoire:

Table.Where(somePredicate)
     .Select(someProjection)
     .AsEnumerable()
     .SomethingElse()

versus

((IEnumerable<SomeProjectionType>)Table.Where(somePredicate)
                                       .Select(someProjection))
                                       .SomethingElse()

Maintenant, quant à la raison pour laquelle une telle méthode est utile, pensez à l'exemple d'un Table dans un LINQ to SQL DataContext. Comme Table est un IQueryable il implémente IEnumerable. Lorsque vous appelez une méthode Where sur une telle Table et que vous énumérez les résultats, du code est exécuté qui finit par entraîner l'exécution d'une instruction SQL sur un serveur SQL. Ce que fait AsEnumerable, c'est: non, je ne veux pas utiliser le fournisseur LINQ to SQL pour exécuter Where, je veux utiliser l'implémentation LINQ to Objects de Where.

Énumérant ainsi

Table.Where(somePredicate)

provoque l'exécution d'une requête sur un serveur SQL Server lors de l'énumération sur

Table.AsEnumerable().Where(somePredicate)

place la table représentée par Table en mémoire et exécute la fonctionnalité Where en mémoire (et pas sur SQL Server!)

C'est le but de AsEnumerable: pour vous permettre de masquer une implémentation spécifique des méthodes IEnumerable et d'utiliser à la place l'implémentation standard.

84
jason

J'ai pensé à une raison autre que la lisibilité, bien que liée à l'implémentation des requêtes: utiliser Linq to Objects sur des types anonymes retournés via un autre fournisseur Linq. Vous ne pouvez pas transtyper vers un type anonyme (ou une collection de types anonymes), mais vous pouvez utiliser .AsEnumerable() pour effectuer le transtypage pour vous.

Exemple:

// Get an IQueryable of anonymous types.
var query = from p in db.PeopleTable /* Assume Linq to SQL */
            select new { Name = p.Name, Age = p.Age };

// Execute the query and pull the results into an IEnumerable of anonymous types
var enum = query.AsEnumerable();

// Use Linq to Objects methods to further refine.
var refined = from p in enum
              select new
              {
                  Name = GetPrettyName(p.Name),
                  DOB = CalculateDOB(p.Age, DateTime.Now)
              };

De toute évidence, la raison ici est que nous voulons utiliser quelque chose comme Linq to SQL pour dérouler certains enregistrements dans un type anonyme, puis effectuer une logique personnalisée (qui ne serait pas possible via Linq to SQL) en utilisant Linq to Objects sur le client - côté.

La conversion en IEnumerable<_anon> N'est pas possible, donc .AsEnumerable() est la seule façon de procéder.

Merci à tous ceux qui ont répondu pour m'aider à reconstituer cela. =)

15
Erik Forbes

Comme je lis le livre C# 6.0 in a Nutshell. Voici un exemple de AsEnumerable dans le livre.


Le but est de lancer un IQueryable<T> séquence à IEnumerable<T>, forçant les opérateurs de requête suivants à se lier aux opérateurs Enumerable au lieu des opérateurs Queryable. Cela provoque l'exécution du reste de la requête localement.

Pour illustrer, supposons que nous avions une table MedicalArticles dans SQL Server et que nous voulions utiliser LINQ to SQL ou EF pour récupérer tous les articles sur la grippe dont le résumé contenait moins de 100 mots. Pour ce dernier prédicat, nous avons besoin d'une expression régulière:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

var query = dataContext.MedicalArticles
            .Where (article => article.Topic == "influenza" &&
            wordCounter.Matches (article.Abstract).Count < 100);

Le problème est que SQL Server ne prend pas en charge les expressions régulières, de sorte que les fournisseurs LINQ-to-db lèveront une exception, se plaignant que la requête ne peut pas être traduite en SQL. Nous pouvons résoudre ce problème en interrogeant en deux étapes: en récupérant d'abord tous les articles sur la grippe via une requête LINQ to SQL, puis en filtrant localement les résumés de moins de 100 mots:

Regex wordCounter = new Regex (@"\b(\w|[-'])+\b");

IEnumerable<MedicalArticle> sqlQuery = dataContext.MedicalArticles
    .Where (article => article.Topic == "influenza");

IEnumerable<MedicalArticle> localQuery = sqlQuery
    .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

Avec AsEnumerable, nous pouvons faire de même en une seule requête:

var query = dataContext.MedicalArticles
      .Where (article => article.Topic == "influenza")
      .AsEnumerable()
      .Where (article => wordCounter.Matches (article.Abstract).Count < 100);

Une alternative à l'appel à AsEnumerable consiste à appeler ToArray ou ToList. L'avantage d'AsEnumerable est qu'il ne force pas l'exécution immédiate des requêtes, ni ne crée de structure de stockage.

4
Timeless

S'il existe une méthode sur un objet qui porte le même nom qu'une méthode d'extension Linq, elle masque la méthode d'extension. L'utilisation de AsEnumerable vous permet d'accéder à l'extension.

Cela semble être nouveau dans SP1.

Hier, j'avais une ligne de code qui a extrait les identifiants des membres d'une table de données: -

var lMmIds = new List<int>(
    lDmMember.DataTable.Select(R => R.MmId)
);

qui a très bien fonctionné jusqu'à ce que j'installe SP1. Maintenant, cela ne fonctionnera que s'il lit

var lMmIds = new List<int>(
    lDmMember.DataTable.AsEnumerable().Select(R => (int)((dsMtables.tbMMemberRow)R).MmId)
);

Edit: j'ai trouvé la vraie raison

C'est pour que vous puissiez utiliser à la fois des méthodes distantes (par exemple WHERE dans une instruction SQL) et des méthodes locales dans la même instruction linq. Sans utiliser AsEnumerable (c'est-à-dire simplement couler), le générateur de requêtes tentera de créer une arborescence d'expression pour une exécution à distance contenant la méthode locale. Si vous insérez AsEnumerable dans la requête, le reste de cette requête sera exécuté localement sur les résultats de la requête distante.

De https://msdn.Microsoft.com/en-us/library/bb335435 (v = vs.110) .aspx

Un type de table qui représente une table de base de données peut avoir une méthode Where qui prend l'argument de prédicat comme une arborescence d'expression et convertit l'arborescence en SQL pour une exécution à distance. Si l'exécution à distance n'est pas souhaitée, par exemple parce que le prédicat appelle une méthode locale, la méthode AsEnumerable peut être utilisée pour masquer les méthodes personnalisées et rendre à la place les opérateurs de requête standard disponibles.

3
Richard Petheram

C'est juste un moyen le plus agréable et le plus court de caster vers un IEnumerable. Si vous le regardez dans Reflector, vous pouvez voir qu'il ne fait rien sauf renvoyer l'objet en tant qu'IEnumerable.

Depuis MSDN:

La méthode AsEnumerable (Of TSource) (IEnumerable (Of TSource)) n'a d'autre effet que de changer le type de source au moment de la compilation d'un type qui implémente IEnumerable (Of T) en IEnumerable (Of T) lui-même.

3
Meta-Knight

Les types anonymes sont une raison principale de fournir ce type de méthodes d'extension. (vous ne pouvez pas utiliser de types anonymes dans les paramètres génériques) Mais un appel de méthode peut utiliser l'inférence de type vous permettant d'omettre de spécifier le type dans les paramètres génériques.

3
james

Comme vous le dites, si un type implémente déjà IEnumerable<T> alors il n'y a pas vraiment de différence fonctionnelle entre la conversion vers l'interface ou l'appel de la méthode AsEnumerable.

Ma supposition, et ce n'est qu'une supposition, est que l'appel de AsEnumerable améliore la lisibilité et conserve la signature fluide des autres méthodes d'extension LINQ:

var query = ((IEnumerable<YourType>)yourCollection).Select(x => x.YourProperty);

// vs

var query = yourCollection.AsEnumerable().Select(x => x.YourProperty);

Il autorise également les types qui n'implémentent pas IEnumerable<T> - par exemple, DataTable - pour avoir sa propre version de l'extension AsEnumerable. Cela vous permet de continuer à utiliser le même modèle dans les requêtes sur ces types - même s'il s'agit d'une méthode AsEnumerable différente que vous appelez - sans avoir à se soucier de savoir si le type implémente vraiment IEnumerable<T>.

2
LukeH