2009-12-08 17 views
7

La siguiente LINQ declaración:¿Por qué esta consulta LINQ-to-SQL obtiene una NotSupportedException?

public override List<Item> SearchListWithSearchPhrase(string searchPhrase) 
{ 
    List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase); 

    using (var db = Datasource.GetContext()) 
    { 
     return (from t in db.Tasks 
       where searchTerms.All(term => 
        t.Title.ToUpper().Contains(term.ToUpper()) && 
        t.Description.ToUpper().Contains(term.ToUpper())) 
       select t).Cast<Item>().ToList(); 
    } 
} 

me da este error :

System.NotSupportedException: secuencia local no se puede utilizar en LINQ to SQL implementación de operadores de consulta, excepto el operador Contiene().

Mirando a su alrededor parece que mi única opción es conseguir todos mis artículosprimera en una lista genérica, y luego hacer una consulta LINQ en eso.

¿O hay una forma ingeniosa de reformular la instrucción LINQ-to-SQL anterior para evitar el error?

RESPUESTA:

Gracias Randy, su idea me ayudó a construir la siguiente solución. No es elegante, pero resuelve el problema y, dado que se generará un código, puedo manejar hasta, p. 20 términos de búsqueda sin ningún trabajo adicional:

public override List<Item> SearchListWithSearchPhrase(string searchPhrase) 
{ 
    List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase); 

    using (var db = Datasource.GetContext()) 
    { 

     switch (searchTerms.Count()) 
     { 
      case 1: 
       return (db.Tasks 
        .Where(t => 
         t.Title.Contains(searchTerms[0]) 
         || t.Description.Contains(searchTerms[0]) 
         ) 
        .Select(t => t)).Cast<Item>().ToList(); 
      case 2: 
       return (db.Tasks 
        .Where(t => 
         (t.Title.Contains(searchTerms[0]) 
         || t.Description.Contains(searchTerms[0])) 
         && 
         (t.Title.Contains(searchTerms[1]) 
         || t.Description.Contains(searchTerms[1])) 
         ) 
        .Select(t => t)).Cast<Item>().ToList(); 
      case 3: 
       return (db.Tasks 
        .Where(t => 
         (t.Title.Contains(searchTerms[0]) 
         || t.Description.Contains(searchTerms[0])) 
         && 
         (t.Title.Contains(searchTerms[1]) 
         || t.Description.Contains(searchTerms[1])) 
         && 
         (t.Title.Contains(searchTerms[2]) 
         || t.Description.Contains(searchTerms[2])) 
         ) 
        .Select(t => t)).Cast<Item>().ToList(); 
      default: 
       return null; 
     } 
    } 
} 
+0

Tenga en cuenta que este no es un error "no implementado", es un error de "secuencia local no se puede utilizar". – Lucas

+0

gracias, solucionado –

+0

No estoy seguro de si estoy leyendo esto correctamente, hay muchos paréntesis, pero no veo una cláusula where bien formada. Your searchTerms.All() está devolviendo una lista de cadenas que no forma una cláusula where, de ahí el error. – Lazarus

Respuesta

1

Ed, he encontrado una situación similar. El código está abajo. La línea de código importante es donde establezco la variable memberList. Vea si esto se ajusta a su situación. Lo siento si el formato no salió bien.

Randy

// Get all the members that have an ActiveDirectorySecurityId matching one in the list. 
IEnumerable<Member> members = database.Members 
    .Where(member => activeDirectoryIds.Contains(member.ActiveDirectorySecurityId)) 
    .Select(member => member); 

// This is necessary to avoid getting a "Queries with local collections are not supported" 
//error in the next query.  
memberList = members.ToList<Member>(); 

// Now get all the roles associated with the members retrieved in the first step. 
IEnumerable<Role> roles = from i in database.MemberRoles 
    where memberList.Contains(i.Member) 
    select i.Role; 
+0

Lo siento Randy, pero no entendiste el punto aquí. – Lazarus

1

Puesto que no puede unirse a la secuencia local con mesa de LINQ, la única manera de traducir la consulta anterior en SQL woluld ser la creación de la cláusula WHERE con nada menos que como las condiciones, ya que hay elementos en la lista searchTerms (concatenado con operadores AND). Aparentemente, linq no hace eso automáticamente y lanza una expiación en su lugar. Pero se puede hacer manualmente por iteración a través de la secuencia:

public override List<Item> SearchListWithSearchPhrase(string searchPhrase) 
{ 
    List<string> searchTerms = StringHelpers.GetSearchTerms(searchPhrase); 

    using (var db = Datasource.GetContext()) 
    { 
     IQueryable<Task> taskQuery = db.Tasks.AsQueryable(); 
     foreach(var term in searchTerms) 
     { 
       taskQuery = taskQuery.Where(t=>t.Title.ToUpper().Contains(term.ToUpper()) && t.Description.ToUpper().Contains(term.ToUpper()))    
     } 
     return taskQuery.ToList(); 
    } 
} 

cuenta que la consulta sigue es ejecutado por DBMS como una instrucción SQL. El único inconveniente es que la lista de términos de búsqueda no debe ser larga, de lo contrario, la declaración SQL producida no será eficiente.

+0

esto se ve bien, pero no consigo que funcione, sigo recibiendo: el texto del tipo de datos de argumento no es válido para el argumento 1 de la función superior. –

+0

Si las columnas y el título de la base de datos tienen el tipo TEXTO, la mayoría de los operadores como CONTAINS ('me gusta') y probablemente TOUPPER no funcionarán. Las columnas TEXT deben consultarse con el índice de búsqueda de texto completo y los procedimientos almacenados en lugar de linq. Entonces, use FTS (http: // msdn.microsoft.com/en-us/library/ms142571.aspx) o cambie las columnas de TEXT a NVARCHAR (MAX). (Estoy hablando de servidor SQL) – PanJanek

Cuestiones relacionadas