2009-03-30 1 views
7

Ich habe einen Linq-Provider, der erfolgreich Daten aus meiner ausgewählten Datenquelle bezieht, aber was ich jetzt tun möchte, dass ich meine gefilterte Ergebnismenge habe, ist erlaubt Linq to Objekte, um den Rest der Ausdrucksbaumstruktur zu verarbeiten (für Dinge wie Joins, Projektion usw.)Teil einer IQueryable-Abfrage durchführen und den Rest an Linq für Objekte übergeben

Mein Gedanke war, dass ich die Expression-Konstante, die meinen IQueryProvider enthält, einfach mit den Resultsets IEnumerable über einen ExpressionVisitor ersetzen konnte gib diesen neuen Ausdruck zurück. Auch das Rück der IEnumerable des Anbieters von meinem IQueryable ... aber das scheint nicht Idee

bearbeiten :-(

zu arbeiten: Einige gute Antworten hier, aber angesichts der Form ...

In meinem Provider kann ich einfach die 2 Resultsets von Kunden und Bestellungen zurückbekommen, wenn die Daten von einer SQL-Quelle stammten, würde ich nur die SQL-Join-Syntax konstruieren und weiterleiten, aber in diesem Fall stammen die Daten nicht von a SQL-Quelle, also muss ich den Join-Code machen ... aber wie gesagt, ich habe die 2 Ergebnismengen, und Linq to Objects kann einen Join machen ... (und später die Projektion) wäre es wirklich schön, einfach die Expression-Konstanten MyProv.Table<Customer> und MyProv.Table<Order> durch List<Customer> und List<Order> zu ersetzen und einen List<>-Provider den Ausdruck verarbeiten zu lassen ... ist das möglich? Wie?

Antwort

3

die Art der Sache, dass ich nach <> konstante im Ausdrucksbaum mit einem konkreten IEnumerable (oder IQueryable die Abfragbare wurde ersetzt durch .AsQueryable()) Ergebnismenge ... Dies ist ein komplexes Thema, das wahrscheinlich nur für Linq Provider-Autoren Sinn macht, die knietief in Expressionsbaumbesuchern etc. sind.

Ich habe ein Snippet auf der msdn Walkthrough gefunden, das etwas tut wie das, was ich nach bin, gibt mir diese einen Weg nach vorn ...

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; 
     } 
    } 
} 
+0

ich dir von meinen Knien danken - tief im Ausdrucksbaum Besucher. :) Das ist genau das, was ich brauchte – Rik

1

Wenn ich nicht falsch verstanden habe, füge ich einfach .ToArray() in die Kette der linq-Methoden an dem Punkt ein, an dem der linq-Provider ausgeführt werden soll.

Zum Beispiel (man denke 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}); 

So durch SortiertNach() wird in SQL übersetzt, aber die Select() ist LINQ to Objects.

0

Robs Antwort ist gut, aber erzwingt volle Aufzählung. Sie könnten werfen Extension-Methode Syntax und lazy evaluation zu halten:

var res = ((IEnumerable<Foo>)dc.Foos 
      .Where(x => x.Bla > 0)) // IQueryable 
      .Where(y => y.Snag > 0) // IEnumerable 
5

Beide der vorherigen Antworten arbeiten, aber es liest besser, wenn man AsEnumerable() verwenden, die IQueryable zu IEnumerable werfen:

// 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 
1

Wenn Sie ein Repository Pattern implementiert Sie weg mit nur Bereitstellung eines IQueryable zurück und abstrahieren den Tisch zu bekommen.

Beispiel:

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 
      } 

und dann nur bauen Ihren Provider das IQueryable Muster in Ihrer Repository-Methode zu modellieren wie this article zeigt.

Auf diese Weise können Sie alle Arten von Anbietern für alles, was Sie benötigen, verwenden. Sie können einen LINQ 2-SQL-Provider haben oder einen In-Memory-Provider für Ihre Komponententests schreiben.

Die Repository-Methode für den SQL-Anbieter LINQ 2 würde wie folgt aussehen:

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

Sehr interessant, ich werde mit diesem Wochenende spielen. –

Verwandte Themen