Exécution d'une partie d'une requête IQueryable et reporter le reste à LINQ pour les objets

StackOverflow https://stackoverflow.com/questions/695678

Question

J'ai un fournisseur Linq qui va avec succès et obtient les données de mon datasource choisi, mais ce que je voudrais faire maintenant que j'ai mon resultset filtrée, est de permettre à Linq d'objets à traiter le reste de l'arbre d'expression (pour des choses comme jointures, projection, etc.)

Ma pensée était que je pouvais remplacer la constante d'expression qui contient mon IQueryProvider avec le résultat ensembles IEnumerable via un ExpressionVisitor puis retourner cette nouvelle expression. retour également fournisseur de IEnumerable de mon IQueryable ... mais cela ne semble pas fonctionner: - (

? De Toute idée

Edit: Quelques bonnes réponses ici, mais étant donné la forme ...

var qry = from c in MyProv.Table<Customer>()
          Join o in MyProv.Table<Order>() on c.OrderID equals o.ID
          select new 
          {
            CustID = c.ID,
            OrderID = o.ID
          }

Dans mon fournisseur, je peux facilement récupérer les 2 resultsets des clients et des commandes, si les données d'une source SQL Je voudrais juste construire et transmettre le SQL syntaxe de jointure, mais ce cas, les données ne provient pas d'un SQL la source, alors je dois faire la jointure dans le code ... mais comme je l'ai dit je les ensembles 2 résultat et LINQ to Objects peut faire une jointure ... (et plus tard la projection), il serait vraiment bien de remplacer seulement la constantes d'expression MyProv.Table<Customer> et MyProv.Table<Order> avec List<Customer> et List<Order> et laissez un processus de fournisseur de List<> l'expression ... est-ce possible? comment?

Était-ce utile?

La solution 2

Le genre de chose que je remplaçais après la Queryable <> constante dans l'arbre d'expression avec un IEnumerable en béton (ou IQueryable via .AsQueryable ()) jeu de résultats ... ceci est un sujet complexe qui fait probablement tout sens aux écrivains du fournisseur Linq qui sont jusqu'aux genoux dans l'expression des visiteurs d'arbres etc.

J'ai trouvé un extrait sur la procédure pas à pas msdn qui fait quelque chose comme ce que je suis après, cela me donne une voie à suivre ...

using System;
using System.Linq;
using System.Linq.Expressions;

namespace LinqToTerraServerProvider
{
    internal class ExpressionTreeModifier : ExpressionVisitor
    {
        private IQueryable<Place> queryablePlaces;

        internal ExpressionTreeModifier(IQueryable<Place> places)
        {
            this.queryablePlaces = places;
        }

        internal Expression CopyAndModify(Expression expression)
        {
            return this.Visit(expression);
        }

        protected override Expression VisitConstant(ConstantExpression c)
        {
            // Replace the constant QueryableTerraServerData arg with the queryable Place collection.
            if (c.Type == typeof(QueryableTerraServerData<Place>))
                return Expression.Constant(this.queryablePlaces);
            else
                return c;
        }
    }
}

Autres conseils

Les deux réponses précédentes le travail, mais il lit mieux si vous utilisez AsEnumerable () pour lancer le IQueryable à IEnumerable:

// Using Bob's code...
var result = datacontext.Table
   .Where(x => x.Prop == val)
   .OrderBy(x => x.Prop2)
   .AsEnumerable()  //  <---- anything after this is done by LINQ to Objects
   .Select(x => new { CoolProperty = x.Prop, OtherProperty = x.Prop2 });

EDIT:

// ... or MichaelGG's
var res = dc.Foos
           .Where(x => x.Bla > 0)  // uses IQueryable provider
           .AsEnumerable()
           .Where(y => y.Snag > 0); // IEnumerable, uses LINQ to Objects

Si vous mis en place un modèle de référentiel vous pourriez sortir avec juste fournir un retour IQueryable et abstraire la table.

Exemple:

var qry = from c in MyProv.Repository<Customer>()
          Join o in MyProv.Repository<Order>() on c.OrderID equals o.ID
          select new 
          {
            CustID = c.ID,
            OrderID = o.ID
          }

et puis juste construire votre fournisseur pour modéliser le modèle IQueryable dans votre méthode Repository comme cet article illustre.

De cette façon, vous pouvez écrire toutes sortes de fournisseurs à utiliser pour tout ce que vous avez besoin. Vous pouvez avoir un LINQ 2 fournisseur de SQL, ou écrire un fournisseur dans la mémoire pour vos tests unitaires.

La méthode du référentiel pour le fournisseur SQL LINQ 2 ressemblerait à quelque chose comme ceci:

public IQueryable<T> Repository<T>() where T : class
{
    ITable table = _context.GetTable(typeof(T));
    return table.Cast<T>();
}

À moins que je suis malentendu, je généralement juste ajouter .ToArray () dans la chaîne des méthodes de LINQ au point où je veux que le fournisseur de LINQ à exécuter.

Par exemple (pensez LINQ to SQL)

var result = datacontext.Table
   .Where(x => x.Prop == val)
   .OrderBy(x => x.Prop2)
   .ToArray()
   .Select(x => new {CoolProperty = x.Prop, OtherProperty = x.Prop2});

par OrderBy () se traduit en SQL, mais le Select () est LINQ aux objets.

La réponse de Rob est bonne, mais force énumération complète. Vous pourriez lancer garder la syntaxe de méthode d'extension et d'évaluation paresseuse:

var res = ((IEnumerable<Foo>)dc.Foos
            .Where(x => x.Bla > 0))  // IQueryable
          .Where(y => y.Snag > 0)   // IEnumerable
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top