2010-10-28 37 views
8

Estoy escribiendo lo que creo que debería ser una aplicación de Windows Forward relativamente simple. Estoy usando LINQ to SQL, aunque nunca lo he usado antes. Tenemos una base de datos de SQL Server, y estoy creando un front-end para acceder a esa base de datos. Estoy tratando de encontrar la manera más eficiente de buscar múltiples parámetros (arbitrarios) de búsqueda con él.Múltiples parámetros de búsqueda con LINQ

En el formulario de Windows, creo un diccionario con cada clave de búsqueda y su valor para buscar, y lo paso a mi método de búsqueda(). Estoy tratando de encontrar una forma de buscar en la base de datos con cada una de esas claves y sus valores asociados. Esto es lo que estoy tratando de hacer:.

public IQueryable<Product> Search(Dictionary<string, string> searchParams) 
{ 
    DBDataContext dc = new DBDataContext(); 
    var query = dc.Products; 

    foreach (KeyValuePair<string, string> temp in searchParams) 
    { 
     query = query.Where(x => x.(temp.Key) == temp.Value); 
    } 

    return query; 
} 

Soy consciente de que sintácticamente x (temp.Key) es incorrecta, pero espero que ilustra lo que estoy tratando de hacer. Me preguntaba si hay otra manera de hacer lo que estoy tratando de hacer sin tener que hacer una declaración de cambio gigante (o si/else si es un árbol).

EDITAR

Por lo tanto, lo revisé un poco, pero todavía tengo problemas con él. Esto es lo que tengo actualmente:

public IQueryable<Product> Search(Dictionary<string, string> searchParams) 
{ 
    DBDataContext dc = new DBDataContext(); 

    string sQuery = ""; 
    foreach (KeyValuePair<string, string> temp in searchParams) 
    { 
     sQuery += temp.Key + "=" + temp.Value + " AND "; 
    } 

    var query = dc.Products.Where(sQuery); 

    return query; 
} 

De acuerdo con el artículo LINQ Dynamic Query Library, esto debería estar bien. Aquí está el error que obtengo:

Los argumentos de tipo para el método 'System.Linq.Queryable.Where (System.Linq.IQueryable, System.Linq.Expressions.Expression>)' no se pueden deducir del uso. Intente especificar los argumentos de tipo explícitamente.

+0

¿Hay una razón por la que el parámetro Método de búsqueda tiene que ser un diccionario en lugar de un Func ??? –

+0

No estoy del todo seguro de lo que quiere decir con eso ... ¿podría aclarar? – jnevelson

+0

Cada tecla en el diccionario contiene los parámetros para buscar (y el valor de cada parámetro para buscar). – jnevelson

Respuesta

2

Si no se requiere el Diccionario por alguna razón, me gustaría hacer su método de búsqueda de la siguiente manera:

public IQueryable<Product> Search(Func<Product, bool> isMatch) 
{ 
    DBDataContext dc = new DBDataContext(); 
    return dc.Products.Where(isMatch).AsQueryable(); 
} 

A continuación, se utiliza el método de esta manera:

Obj.Search(item => item.Property1 == "Hello" && item.Property2 == "World"); 

¿Hay alguna ¿Por qué no puedes hacer eso?

[Editar: se ha añadido AsQueryable()]

[Editar: Consulta dinámica para el uso de cadenas]

Echa un vistazo aquí y ver si esto ayuda. No he utilizado, pero parece que es lo que está buscando: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Personalmente, yo generalmente prefieren la expresión de tipo seguro> enfoque, ya que esto le dará a compilar los errores de tiempo ... pero si se necesitan cadenas, entonces parece ser la mejor manera de hacerlo.

Basado en el enlace anterior, parece que debería ser capaz de hacer:

query = query.Where(String.Format("{0}={1}",dict.Key,dict.Value)); 

[Editar: String Ejemplo de construcción]

Por lo tanto, uno de los problemas es que la consulta SQL va a terminar con una Y al final de la cadena, pero luego no hay ninguna condición después ... entonces, podría intentar cambiar a esto ...la sintaxis podría ser levemente, pero debe estar en lo cierto:

public IQueryable<Product> Search(Dictionary<string, string> searchParams) 
{ 
    DBDataContext dc = new DBDataContext(); 

    StringBuilder sQuery = new StringBuilder(); 
    foreach (KeyValuePair<string, string> temp in searchParams) 
    { 
     if(sQuery.Length > 0) sQuery.Append(" AND "); 
     sQuery.AppendFormat("{0}={1}",temp.Key,temp.Value); 
    } 

    var query = dc.Products.Where(sQuery.ToString()); 

    return query; 
} 

Esto sólo se utilizará "Y" en condiciones después de la primera. Espero que ayude ...

FYI - Es fuera de tema, pero 'por qué' Solía ​​StringBuilder es que la concatenación de cadenas de la manera que tuvo que daría lugar a la cadena ser destruida y una nueva cadena que se crea en la memoria 4 veces por ciclo ... cambiado a un generador de cadenas, ya que creará un búfer que se puede llenar y cambiar de tamaño solo cuando sea necesario.

+0

Su función devolverá 'IEnumerable', no' IQueryable', por lo que el código no se compilará. Además, las filas de la base de datos se filtrarán en el código C#, no en la capa de la base de datos. Será más lento y requerirá más memoria. – Athari

+0

Agregó AsQueryable() para que compile –

+0

Parece que Athari sabe algo acerca del aspecto Expression <>, sin embargo, que yo no ... así que probablemente sea mejor ir con su respuesta. –

0

En lugar de utilizar el diccionario, puede utilizar este método:

using System.Linq.Expressions; 
// ... 

public static IQueryable<Product> Search(Expression<Func<Product, bool>> search) 
{ 
    DBDataContext dc = new DBDataContext(); 
    return dc.Products.Where(search); 
} 

Usted puede utilizar de esta manera:

Search(p => p.Name == "Name" && p.Id == 0); 

También no se limitará a las propiedades de cadena.

+0

Entonces, ¿qué está diciendo es que para Linq a SQL necesita usar Expression para poder ejecutar la consulta en la base de datos? Simplemente haciendo Func <> se ejecutará en C#? –

+0

Esto todavía tiene la misma limitación inherente que estoy tratando de evitar. Todavía necesito escribir explícitamente la propiedad p.Name, en lugar de tenerla generada dinámicamente en función de las claves que estén presentes en el Diccionario (p. [KEY] = [VALUE]). No estoy de ninguna manera ligado o apegado al uso de un diccionario, pero me pareció que era el único método para almacenar claves para buscar y sus valores. – jnevelson

+0

bien, por lo que el diccionario era importante ... He añadido un enlace a mi respuesta sobre el uso de cadenas utilizando biblioteca de consultas MS dinámico –

3

Aquí hay un ejemplo que funciona (yo sólo probé), utilizando el Dynamic LINQ Query Library.

using System.Linq.Dynamic; 
// ... 

Dictionary<string, string> searchParams = new Dictionary<string,string>(); 

searchParams.Add("EmployeeID", "78"); 
searchParams.Add("EmpType", "\"my emp type\""); 

IQueryable<Employee> query = context.Employees; 

foreach (KeyValuePair<string, string> keyValuePair in searchParams) 
{ 
    query = query.Where(string.Format("{0} = {1}", keyValuePair.Key, keyValuePair.Value)); 
} 

List<Employee> employees = query.ToList(); 

Y, para que sea absolutamente claro que este código realmente funciona aquí es la real SQL generado:

FROM [HumanResources].[Employee] AS [t0] 
WHERE ([t0].[EmpType] = @p0) AND ([t0].[EmployeeID] = @p1)',N'@p0 nvarchar(11),@p1 int',@p0=N'my emp type',@p1=78 
+0

Bueno, finalmente se compila, por lo que es un gran primer paso. MUCHAS GRACIAS a usted y a Kevin Nelson. Problema diferente ahora: cuando ingreso un valor para buscar, se lanza una excepción en DynamicLibrary de que no existe ninguna propiedad o campo [valor que busqué] en 'Productos'. Entonces parece que en lugar de buscar el valor en la clave de cada entrada, está buscando cada entrada para un campo que es el valor que quiero buscar. Si estoy buscando un nombre, está buscando Employee.jonathan = first_name, en lugar de Employee.first_name = jonathan. – jnevelson

+0

He intentado cambiar el orden de la cadena de consulta a query = query.Where (string.Format ("{0} = {1}", keyValuePair.Value, keyValuePair.Key)); y todavía arroja la misma excepción. – jnevelson

+0

En el depurador, parece que no importa el orden de la clave y el valor en el diccionario, DynamicLibrary siempre busca el valor en lugar de la clave. – jnevelson

0

han u tratar de poner una sola cotización para su cadena? basado en el bucle foreach, habrá Extra 'y' al final de la consulta

probar este lugar

string sQuery = searchParam.Select(entry => string.Format("{0} = '{1}'", entry.Key, entry.Value)).Aggregate((current, next) => current + " AND " next); 
0

si usted está dispuesto a crear un diccionario de términos de búsqueda que he usado algo cerca del enfoque a continuación. He adaptado lo que más me gusta de la forma en que hacían las cosas, también cambié los nombres de las tablas para adaptarlas a un modelo de contexto/objeto de datos que tengo a mi disposición.

La idea es crear una lista con clave de términos que su consulta soporta la búsqueda por. A continuación, agregue funciones que devuelven una expresión que luego se puede pasar a la cláusula where de su consulta.

Usted debe agregar por supuesto, algunos de manipulación de las claves no válidas etc. error

public IQueryable<Person> Search(Dictionary<string, string> searchParams) 
{ 
    DBDataContext dc = new DBDataContext(); 
    var query = dc.Persons.Where(p => true); //do an 'empty predicate' because you want 'query' to be an iqueryable<Person> not a Table<Person> 

    //build a list of the types of things you can filter on. 
    var criteriaDefinitions = new Dictionary<string,Func<string,Expression<Func<Person,bool>>>>(); 
    criteriaDefinitions.Add("FirstName",s => p => p.FirstName == s); 
    criteriaDefinitions.Add("LastName",s => p => p.LastName == s); 

    //you can do operations other than just equals 
    criteriaDefinitions.Add("EmailContains",s => p => p.Email.Contains(s)); 

    //you can even create expressions that integrate joins. 
    criteriaDefinitions.Add("HasContactInCity",s => p => p.Contacts.Any(c => c.City == s)); 


    foreach (KeyValuePair<string, string> temp in searchParams) 
    { 
     //grab the correct function out of the dictionary 
     var func = criteriaDefinitions[temp.Key]; 

     //evaluating the function will return an expression which can passed into the where clause. 
     var expr = func(temp.Value); 
     query = query.Where(expr); 
    } 

    return query; 
} 
Cuestiones relacionadas