2011-02-10 17 views
14

Tengo usuarios buscando registros de tipo Record. Teclean un término de búsqueda en un cuadro de texto y luego busco registros haciendo coincidir varios campos con el término de búsqueda.Cómo reutilizar cláusulas where en consultas de Linq a SQL

Mi consulta es así:

var results = from record in DataContext.Records 
       where 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
       select record; 

Tengo un número de consultas que todos utilizan el mismo filtro y por lo tanto me gustaría extraer el filtrado para que pueda ser reutilizado. Algo así como:

var filter = new Func<Record, string, bool>(
       (record, term) => 
        record.Field1.ToLower().Contains(term) || 
        record.Field2.ToLower().Contains(term) || 
        record.Field3.ToLower().Contains(term) 
      ); 

var results = from record in DataContext.Records 
       where filter(record, term) 
       select record; 

Sin embargo, esto no funcionará porque:

Método 'System.Object DynamicInvoke (System.Object [])' no tiene traducción apoyado a SQL.

¿Cómo puedo volver a utilizar mi condición de estado en las consultas?

Respuesta

12

Utilice un CompiledQuery!

var filter = CompiledQuery.Compile(
    (DatabaseDataContext dc, Record record, string term) => 
     record.Field1.ToLower().Contains(term) || 
     record.Field2.ToLower().Contains(term) || 
     record.Field3.ToLower().Contains(term) 
); 

var results = from record in DataContext.Records 
       where filter(DataContext, record, term) 
       select record; 

Para obtener más información, vea How to: Store and Reuse Queries.

+0

No creo que esto funcione como está escrito: CQ.Compile produce un Func por lo que no se puede usar en la cláusula Where de una consulta de LINQ a SQL. ¿No necesita algo como 'var query = CompiledQuery.Compile ((string term) => de r en DataContext.Records donde r.Field1.ToLower(). Contiene (term) select r);' then 'var results = query ("someTerm"); '? ¡No he usado CompiledQuery así que puedo estar equivocado! – itowlson

+0

@itowlson: Funciona. Así es como crea funciones reutilizables utilizables en LINQ to SQL. El proveedor de consultas reconocerá la función compilada y generará la consulta según sea necesario. No solo genera consultas completas, sino que también podría generar partes de consultas (como este condicional). –

+1

Esto funciona como se esperaba. Lo único extraño es que también reutilizo ese filtro en las consultas de Linq To Object, y aún tengo que pasar el Linq To Sql DataContext como parámetro. ¿Alguna forma de definir la consulta independientemente del DataContext? –

1

Creo que necesitas convertirlo en Expression<Func<Record, bool>>. De lo contrario, está tratando de traducir la llamada del método C# real a SQL en lugar de la descripción de la misma. Esto no es una garantía de que esta versión funcionará; No estoy seguro de qué funciones de cadena se pueden traducir a SQL.

12

Es necesario construir una expresión en lugar de una función:

Expression<Func<Record, bool>> filter = 
    record => record.Field1.ToLower().Contains(term); // rest omitted 

La expresión lambda sigue siendo la misma, pero hay que volver en una variable de tipo Expression<Func<Record, bool>> - que hará que el compilador de C# de compilación como una expresión en lugar de un delegado, lo que le permite pasar a LINQ to SQL.

Sin embargo, usted no será capaz de utilizar una variable de expresión con un C# -Sintaxis cláusula where: usted tendrá que usar el método de extensión Dónde:

var results = DataContext.Records.Where(filter); 

Editado para agregar: Si usted quiere ser capaz de crear filtros en diferentes términos, sólo tiene un método para producir una expresión de un término:

private static Expression<Func<Record, bool>> Filter(string term) 
{ 
    return r => r.Field1.ToLower().Contains(term); 
} 

var results = DataContext.Records.Where(Filter(term)); 

Si prefiere mantener filter como lambda y cuando usted tiene en este momento, se puede hazlo, pero los genéricos ser un poco anidado:

Func<string, Expression<Func<Record, bool>>> filter = 
    term => (r => r.Field1.ToLower().Contains(term)); 

var results = DataContext.Records.Where(filter(term)); 

En cualquier caso, lo importante es que lo que sucede en el caso de la cláusula debe ser un Expression<Func<Record, bool>> - pero como se muestra más arriba, puede hacer que la expresión depende de term mediante la construcción de una expresión adecuada en el mosca. Que es exactamente lo que LINQ to SQL estaría haciendo si deletreara el filtro a mano en la cláusula Where.

+0

La sintaxis de C# para la cláusula where es simplemente compilador de azúcar; genera el mismo código. –

+0

Sí, pero si el filtro es de tipo Expresión en lugar de Func, entonces no admite la sintaxis 'filter (record)'. Obtiene el error "el filtro es una variable pero se usa como un método". Y si simplemente escribe 'where filter', entonces 'no puede convertir implícitamente el tipo Expression (...) en bool'. – itowlson

+0

Gracias - tiene sentido. Sin embargo, no logré llevarlo a cabo porque en el ejemplo (incorrecto) que estaba dando, 'término' era un cierre en mi expresión lambda y para reutilizar el filtro necesitaría convertirlo en un parámetro. Ahora mi filtro es un 'Expression >' donde se espera una 'Expression >'. ¿Esto todavía es factible? –

2

Además del problema Expression<Func<Record, bool>> que otros han señalado, sugiero buscar en PredicateBuilder. Es muy bueno para combinar dinámicamente expresiones lambda.

Cuestiones relacionadas