2010-04-06 15 views
7

Sto seguendo i suggerimenti here, cercando di sfruttare l'affermazione che lo sql non viene creato fino a quando l'enumeratore non viene attivato. Tuttavia ottengo il seguente errore sul codice qui sotto. Sto usando Linq2Entities, non linq2sql. C'è un modo per farlo in Linq2entities?Linq SqlMethods.Like non riesce

Il metodo 'Boolean Like (System.String, System.String)' non può essere utilizzato sul client; è solo per la traduzione in SQL.

  query = db.MyTables.Where(x => astringvar.Contains(x.Field1)); 

      if (!String.IsNullOrEmpty(typeFilter)) 
      { 
       if (typeFilter.Contains('*')) 
       { 
        typeFilter = typeFilter.Replace('*', '%'); 
        query = query.Where(x=> SqlMethods.Like(x.Type, typeFilter)); 
       } 
       else 
       { 
        query = query.Where(x => x.Type == typeFilter); 
       } 
      } 

Note: db è un'entità che esegue il mapping su un server SQL.

risposta

4

Si potrebbe fare ESQL e fare qualcosa di simile al seguente ..

 db.MyTables.Where("it.Type like '" + typeFilter + "'").ToList(); 
+0

Questo non può essere compilato sulla mia (anche l'aggiunta del + mancante). Sono in vs2008, .net 3.5 sp1 –

+0

Non importa, deve andare specificamente direttamente alla tabella, non più tardi. Grazie. –

+0

Ora non è possibile risolvere "FieldName" nello scope o nel contesto corrente. –

4

Il SqlMethods class è destinato a essere utilizzato con LINQ to SQL. Quando si usano metodi da esso (che la documentazione pubblica ti dice di non fare, non è per il consumo pubblico), il provider IQueryable per LINQ-to-Entities non sa cosa fare con esso o come tradurlo.

Se tu avessi un singolo carattere jolly all'inizio o alla fine del filtro, allora si potrebbe utilizzare StartsWith o EndsWith metodi sulla classe String, e LINQ to Entities sosterrete questo.

Tuttavia, in questo caso, si dispone di un numero variabile di caratteri jolly, quindi sarà necessario scendere al livello ESQL e creare una query, come indicato in Nix's answer.

11

Non so come si può fare Entity Framework utilizzare il "vero" COME operatore, ma una possibile soluzione potrebbe essere quella di esprimere un'espressione LIKE in termini di StartsWith, Contains e EndsWith

Per esempio:

LIKE 'a%' => StartsWith("a") 
LIKE '%a' => EndsWith("a") 
LIKE '%a%' => Contains("a") 
LIKE 'a%b' => StartsWith("a") && EndsWith("b") 
LIKE 'a%b%' => StartsWith("a") && Contains("b") 

E così via ...

si noti che non è esattamente equivalente ad usare LIKE in SQL: per esempio LIKE '%abc%bcd%' porterebbe a Contains("abc") && Contains("bcd"). Questo corrisponderebbe a "abcd" anche se la condizione LIKE originale non lo sarebbe. Ma per la maggior parte dei casi, dovrebbe essere abbastanza buono.

Ecco un esempio di implementazione, utilizzando PredicateBuilder e LinqKit per costruire le espressioni in base a un modello come:

public static class ExpressionHelper 
{ 
    public static Expression<Func<T, bool>> StringLike<T>(Expression<Func<T, string>> selector, string pattern) 
    { 
     var predicate = PredicateBuilder.True<T>(); 
     var parts = pattern.Split('%'); 
     if (parts.Length == 1) // not '%' sign 
     { 
      predicate = predicate.And(s => selector.Compile()(s) == pattern); 
     } 
     else 
     { 
      for (int i = 0; i < parts.Length; i++) 
      { 
       string p = parts[i]; 
       if (p.Length > 0) 
       { 
        if (i == 0) 
        { 
         predicate = predicate.And(s => selector.Compile()(s).StartsWith(p)); 
        } 
        else if (i == parts.Length - 1) 
        { 
         predicate = predicate.And(s => selector.Compile()(s).EndsWith(p)); 
        } 
        else 
        { 
         predicate = predicate.And(s => selector.Compile()(s).Contains(p)); 
        } 
       } 
      } 
     } 
     return predicate; 
    } 
} 

Ed ecco come si potrebbe usare:

var expr = ExpressionHelper.StringLike<YourClass>(x => x.Type, typeFilter); 
query = query.AsExpandable().Where(expr.Compile()); 

ho appena provato con un semplice modello EF, e sembra funzionare bene :)

+1

purtroppo ho bisogno di un "MI PIACE", ma questa è una soluzione interessante. –

+0

Approccio interessante, anche se 'LIKE '% a% b%' => StartsWith (" a ") && Contains (" b ")' non è vero, poiché all'inizio c'è un punto percentuale in più. 'LIKE 'a% b%'' è migliore. –

0

È possibile utilizzare un reale come in Collegamento alle entità

Aggiungere

<Function Name="String_Like" ReturnType="Edm.Boolean"> 
     <Parameter Name="searchingIn" Type="Edm.String" /> 
     <Parameter Name="lookingFor" Type="Edm.String" /> 
     <DefiningExpression> 
     searchingIn LIKE lookingFor 
     </DefiningExpression> 
    </Function> 

al vostro EDMX in questo tag:

edmx: EDMX/edmx: Runtime/edmx: ConceptualModels/Schema

Ricorda anche lo spazio dei nomi nella Attributo <schema namespace="" />

Quindi aggiungere una classe di estensione nel namespace precedente e:

public static class Extensions 
{ 
    [EdmFunction("DocTrails3.Net.Database.Models", "String_Like")] 
    public static Boolean Like(this String searchingIn, String lookingFor) 
    { 
     throw new Exception("Not implemented"); 
    } 
} 

Questo metodo di estensione verrà ora associato alla funzione EDMX.

Maggiori informazioni qui: http://jendaperl.blogspot.be/2011/02/like-in-linq-to-entities.html