2014-01-22 6 views
5

Ich versuche, eine Stichwortsuche auf einem IQueryable Objekt zu tun, aber gibt es eine Möglichkeit, dies zu tun, ohne es zuerst in eine Liste zu konvertieren?IQueryable mit Foreach auf das Ergebnis

Mein Code:

var customers = Customer.IQueryableAll(); // Method returns IQueryable containing all customers.  
var customerHolder = new List<Customer>(); 

foreach(var k in keywords) //keywords = List<string> 
{ 
    customerHolder.AddRange(customers.Where(x=>x.FirstName.Contains(k) || x.LastName.Contains(k) || x.CompanyName.Contains(k)).ToList()) 
} 

return customerHolder.Distinct(); 

Dies funktioniert, wenn ich auf einmal die Ergebnisse angezeigt werden sollen, aber das Problem kommt, wo ich Paging tun wollen. Die Funktion würde immer noch alle Einträge aus der Datenbank vor dem Paging erhalten, was bei großen Tabellen sehr ineffizient ist. (z. B. customerHolder.Skip(5).Take(5);)

Gibt es eine Möglichkeit, den Foreach-Suchteil in die Abfrage selbst zu integrieren?

dh.

customers.Where(x => x.Name.Contains(anythingInKeyWords)); 

EDIT: Zur weiteren Klarstellung, ich will zu halten die OR in den oben genannten, so Filterung und Nachfilterung mit mehreren where-Klauseln nicht. IE. Bill Job/Bill Gates> Suche Bill Gates sollte beide Einträge zurückgeben, weil Bill Matches.

+0

Die einzige Möglichkeit besteht darin, Filterausdruck dynamisch (zu OR-Bedingungen) zu erstellen. – Dennis

Antwort

5

Sie müssen eine Abfrage erstellen, die das Ergebnis Ihres Filterausdrucks für jedes Schlüsselwort pro Entität OR-verknüpft, was ohne die Verwendung von dynamischem LINQ nicht sehr praktisch ist. Hier ist eine Erweiterung Methode, die genau das für Sie tun:

public static class ExtensionMethods 
{ 
    public static IQueryable<TEntity> TestPerKey<TEntity, TKey>( 
     this IQueryable<TEntity> query, 
     IEnumerable<TKey> keys, 
     Expression<Func<TEntity, TKey, bool>> testExpression) 
    { 
     // create expression parameter 
     var arg = Expression.Parameter(typeof(TEntity), "entity"); 

     // expression body var 
     Expression expBody = null; 

     // for each key, invoke testExpression, logically OR results 
     foreach(var key in keys) 
     { 
      // constant expression for key 
      var keyExp = Expression.Constant(key); 

      // testExpression.Invoke expression 
      var invokeExp = Expression.Invoke(testExpression, arg, keyExp); 

      if(null == expBody) 
      { 
       // first expression 
       expBody = invokeExp; 
      } 
      else 
      { 
       // logically OR previous expression with new expression 
       expBody = Expression.OrElse(expBody, invokeExp); 
      } 
     } 

     // execute Where method w/ created filter expression 
     return query.Where((Expression<Func<TEntity, bool>>)Expression.Lambda(expBody, arg)); 
    } 
} 

Verbrauch:

class TestEntity 
{ 
    public int Id { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string CompanyName { get; set; } 
} 

static void Main() 
{ 
    var testCollection = new TestEntity[]{ 
     new TestEntity(){ 
      Id = 0, 
      FirstName = "abc", 
      LastName = "def", 
      CompanyName = "ghi" 
     }, 
     new TestEntity(){ 
      Id = 1, 
      FirstName = "def", 
      LastName = "ghi", 
      CompanyName = "jkl" 
     }, 
     new TestEntity(){ 
      Id = 2, 
      FirstName = "ghi", 
      LastName = "jkl", 
      CompanyName = "mno" 
     }, 
     new TestEntity(){ 
      Id = 3, 
      FirstName = "bcd", 
      LastName = "efg", 
      CompanyName = "hij" 
     }, 
    }; 

    var keywords = new[]{ 
      "abc", 
      "jkl" 
     }; 

    var query = testCollection.AsQueryable() 
     .TestPerKey( 
      keywords, 
      (t, k) => 
       t.FirstName.Contains(k) || 
       t.LastName.Contains(k) || 
       t.CompanyName.Contains(k)); 

    foreach(var result in query) 
    { 
     Console.WriteLine(result.Id); 
    } 
} 

Update - versuchen, die folgende Erweiterungsmethode. Es ist spezifischer, sollte aber mit EF arbeiten:

+0

Schön, aber leider funktioniert es nicht sofort mit Entity Framework ("Der LINQ-Ausdrucksknotentyp 'Invoke' wird in LINQ to Entities nicht unterstützt."). Sie können es jedoch einrichten, indem Sie Linqkit 'AsExpandable()' auf dem von EF stammenden 'IQueryable' aufrufen. –

+0

ah, stimmt. Sie müssten die Methode spezifischer machen und das gesamte Prädikat dynamisch generieren. – Moho

+0

hat eine weitere Erweiterungsmethode hinzugefügt, die mit EF arbeiten sollte, deren Prädikat jedoch fest codiert ist – Moho

-5

Nein, Sie müssen ToList verwenden, um die gewünschten Ergebnisse zu erhalten, da LINQ vollständig die Terminologie der Sammlungsbasisobjekte darstellt.

+4

Das ist total ** nicht ** wahr ... – MarcinJuraszek

0

Sie müssen vollständige IQueryable Abfrage erstellen und nur am Ende ausführen. Versuchen Sie es mit:

keywords.All(k=>{customers = customers.Where(x=>x.FirstName.Contains(k) || x.LastName.Contains(k) || x.CompanyName.Contains(k)); return true;}); 

var result = customers.ToList(); 
Verwandte Themen