2009-10-12 10 views
5

Tengo un formulario de búsqueda que permite a los usuarios buscar en diferentes campos de diferentes maneras. Aquí hay un ejemplo de mi código.Formulario de búsqueda ASP.NET - ¿Dinámico a SQL?

var claims = from c in db.Claims select c; 

switch (ddlSearchField.Text) 
{ 
    case "StartsWith": 
     claims = claims.Where(c => c.companyFileID.StartsWith(txtSearchBox.Text)); 
     break; 

    case "Equals": 
     claims = claims.Where(c => c.companyFileID == txtSearchBox.Text); 
     break; 

    case "Contains": 
     claims = claims.Where(c => c.companyFileID.Contains(txtSearchBox.Text)); 
     break; 
} 

Tengo cerca de diez diferentes campos que un usuario puede buscar en lo que mi sentencia switch externo es bastante grande. Tiene que haber una forma más elegante de lograr esto.

Respuesta

3

Se podría refactorizar algunas partes del código mediante la creación de un método de extensión. Algo así:

static class QueryableExtensions 
{ 
    private static MethodInfo StringContainsMethod; 
    private static MethodInfo StringStartsWithMethod; 

    static QueryableExtensions() 
    { 
     Type[] singleStringParam = new[] {typeof(string)}; 
     StringContainsMethod = typeof(string).GetMethod("Contains", singleStringParam); 
     StringStartsWithMethod = typeof(string).GetMethod("StartsWith", singleStringParam); 
    } 

    public static IQueryable<T> AppendTextFilter<T>(this IQueryable<T> queryable, Expression<Func<T, string>> memberSelector, string condition, string value) 
    { 
     Expression expression = null; 
     switch (condition) 
     { 
      case "StartsWith": 
       expression = Expression.Call(
           memberSelector.Body, 
           StringStartsWithMethod, 
           Expression.Constant(value)); 
       break; 

      case "Equals": 
       expression = Expression.Equal(
           memberSelector.Body, 
           Expression.Constant(value)); 
       break; 

      case "Contains": 
       expression = Expression.Call(
           memberSelector.Body, 
           StringContainsMethod, 
           Expression.Constant(value)); 
       break; 

      default: 
       throw new NotSupportedException(string.Format("'{0}' is not a supported condition", condition)); 
     } 

     var lambda = Expression.Lambda<Func<T, bool>>(
         expression, 
         memberSelector.Parameters); 
     return queryable.Where(lambda); 
    } 
} 

A continuación, puede utilizarlo como esa:

var claims = db.Claims 
      .AppendTextFilter(c => c.companyFileID, ddlSearchField.Text, txtSearchBox.Text) 
      .AppendTextFilter(c => c.someOtherProperty, ddlOtherSearchField.Text, txtOtherSearchBox.Text) 
      ...; 
+0

Hola Thomas, su solución se ve muy bien y es muy cercano a lo que yo estoy buscando. ¿Es seguro establecer la variable "claims" en un datasource de gridview? Lo intenté y recibí un mensaje de error. –

+0

Lo sentimos, el mensaje de error fue: Método 'System.Object DynamicInvoke (System.Object [])' no tiene traducción soportada a SQL. –

+0

Bien, veo el problema ... Linq to SQL no puede convertir una llamada de delegado a una expresión SQL, por lo que falla. Trataré de encontrar una solución y actualizar mi respuesta –

0

Se podría hacer un diccionario de sus posibles predicados:

Dictionary<string, Func<string, Expression<Func<Claim, bool>>>> map = new Dictionary<string, Func<string, Expression<Func<Claim, bool>>>>() { 
    { "StartsWith", t => c => c.companyFileID.StartsWith(t) }, 
    { "Equals",  t => c => c.companyFileID == t }, 
    { "Contains", t => c => c.companyFileID.Contains(t) } 
}; 

que podría ser utilizado como esto:

var search = ddlSearchField.Text; 
var text = txtSearchBox.Text; 

var claims = from c in db.Claims select c; 

Func<string, Expression<Func<Claim, bool>>> predicate = null; 
if(dict.TryGetValue(search, out predicate)) 
    claims = claims.Where(predicate(text)); 
Cuestiones relacionadas