2011-04-04 31 views
30

Me gustaría saber cuál es la mejor manera de crear consultas dinámicas con Entity Framework y LINQ.la creación de consultas dinámicas con marco de la entidad

Quiero crear un servicio que tiene muchos parámetros para la clasificación y filtrado (más de 50). Obtendré el objeto de la interfaz gráfica de usuario, donde se rellenarán ... y la consulta se ejecutará desde un solo método de servicio.

Miré alrededor y vi que podía crear dinámicamente una cadena que se puede ejecutar al final de mi método. No me gusta mucho de esta manera. ¿Hay una mejor manera de hacer esto? Preferiblemente, escriba safe con cheque de compilación?

Respuesta

45

Se puede componer un paso a paso IQueryable<T>. Asumiendo que tiene una clase FilterDefinition que describe cómo el usuario desea filtrar ...

public class FilterDefinition 
{ 
    public bool FilterByName { get; set; } 
    public string NameFrom { get; set; } 
    public string NameTo { get; set; } 

    public bool FilterByQuantity { get; set; } 
    public double QuantityFrom { get; set; } 
    public double QuantityTo { get; set; } 
} 

... entonces se podría crear una consulta de este modo:

public IQueryable<SomeEntity> GetQuery(FilterDefinition filter) 
{ 
    IQueryable<SomeEntity> query = context.Set<SomeEntity>(); 
    // assuming that you return all records when nothing is specified in the filter 

    if (filter.FilterByName) 
     query = query.Where(t => 
      t.Name >= filter.NameFrom && t.Name <= filter.NameTo); 

    if (filter.FilterByQuantity) 
     query = query.Where(t => 
      t.Quantity >= filter.QuantityFrom && t.Quantity <= filter.QuantityTo); 

    return query; 
} 
+0

Gracias, pero ¿cómo funciona esto? ¿Esto no extrae todos los datos de la base de datos y luego, paso a paso, lo reduce al conjunto de datos deseado? – Eduard

+7

@ t-edd: No, aprovecha 'ejecución diferida' (http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx). Eso significa que 'IQueryable ' que se compone en el ejemplo anterior es solo una expresión de consulta que describe cómo se filtran los datos. La ejecución real de la consulta no está en el ejemplo en absoluto. Entonces ejecuta la consulta aplicando un operador "codicioso" a 'IQueryable ', por ejemplo 'query.ToList()'. En este punto, y no antes, la expresión de consulta se traduce a SQL y se envía al servidor. – Slauma

+0

No es tan bueno porque asume que 'SomeEntity' tiene campos Name y Quantity, por lo que esto es solo mitad dinámico. –

1

Se podría buscar en la creación del servicio a través de WCF Data Services y dinámicamente crear la URI para consultar el modelo de entidad.

30

La única otra forma que yo sepa sería construir IQueryable en función de sus vaues filtro.

public List<Contact> Get(FilterValues filter) 
    { 
     using (var context = new AdventureWorksEntities()) 
     { 
      IQueryable<Contact> query = context.Contacts.Where(c => c.ModifiedDate > DateTime.Now); 

      if (!string.IsNullOrEmpty(filter.FirstName)) 
      { 
       query = query.Where(c => c.FirstName == filter.FirstName); 
      } 

      if (!string.IsNullOrEmpty(filter.LastName)) 
      { 
       query = query.Where(c => c.LastName == filter.LastName); 
      } 

      return query.ToList(); 
     } 
    } 
+0

Sí, pero ¿es este rendimiento efectivo? ¿Cuándo se ejecuta el select? Al final, cuando se llama ToList()? Imagine que tengo un gran conjunto de datos ... – Eduard

+1

No, no es un golpe de rendimiento, ya que utiliza la ejecución diferida para consultar solo una vez. – BrandonZeider

+0

+1 Gracias por una buena respuesta. – Eduard

4

Usted puede utilizar especificación dinámica y dinámico ordenando He blogueado sobre ellos here y here. A continuación debería ayudar ejemplo You-

 //Assume you're getting following values from search form. 
     string userSuppliedProperty = "AverageRating"; 
     OperationType userSuppliedOperationType = OperationType.GreaterThan; 
     var userSuppliedValue = 4.5; 

     //Create DynamicSpecification from these properties and pass it to repository. 
     var userFilter = new DynamicSpecification<Product>(userSuppliedProperty, userSuppliedOperationType, userSuppliedValue); 
     var filteredProducts = _repository.Get(userFilter); 

     //You can also combine two specifications using either And or Or operation 
     string userSuppliedProperty2 = "Category"; 
     OperationType userSuppliedOperationType2 = OperationType.EqualTo; 
     var userSuppliedValue2 = "Keyboard"; 
     var userFilter2 = new DynamicSpecification<Product>(userSuppliedProperty2, userSuppliedOperationType2, userSuppliedValue2); 

     var combinedFilter = userFilter.And(userFilter2); 
     var filteredProducts2 = _repository.Get(combinedFilter); 

     //and it support dynamic sorting 
     string userSuppliedOrderingProperty = "Category"; 
     OrderType userSuppliedOrderType = OrderType.Ascending; 
     var sortedFilteredProducts = _repository.Get(combinedFilter, o => o.InOrderOf(userSuppliedOrderingProperty, userSuppliedOrderType)); 

No sé acerca de la búsqueda de objetos/DTO que está recibiendo, pero se puede crear fácilmente una búsqueda genérica objeto/DTO y puedo asignarlo a la cadena de objetos de GenericSpecification en pocas líneas de código. Lo he usado en el pasado en torno a un servicio de WCF y me ha funcionado muy bien.

Cuestiones relacionadas