2011-04-04 38 views
30

我想知道使用实体框架和linq创建动态查询的最佳方式是什么。使用实体框架创建动态查询

我想创建一个具有许多排序和过滤参数(超过50)的服务。我将从gui中获得这些对象的填充内容,并且将从单个服务方法执行查询。

我环顾四周我看到我可以动态创建一个字符串,可以在我的方法结束时执行。我不太喜欢这种方式。有一个更好的方法吗?最好使用编译检查键入safe?

回答

45

你可以一步一步撰写IQueryable<T>。假设你有一个FilterDefinition类,它描述了用户希望如何过滤...

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; } 
} 

...那么你可以创建一个查询,像这样:

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

谢谢,但是这个工作怎么样?这不是从数据库中提取所有数据,然后逐步缩小到所需的一组数据? – Eduard 2011-04-05 06:36:52

+7

@ t-edd:不,它利用“延迟执行”(http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx)。这意味着在上面的例子中构成的'IQueryable '只是一个描述数据如何过滤的查询表达式。查询的真正执行完全不在本例中。您执行查询,然后通过将“贪婪”运算符应用于'IQueryable ',例如'query.ToList()'。此时 - 而不是更早 - 查询表达式被翻译成SQL并发送到服务器。 – Slauma 2011-04-05 10:03:21

+0

这不是很好,因为它假设'SomeEntity'具有名称和数量字段,所以这只是一半的动态。 – 2017-01-23 17:56:57

1

您可以考虑使用WCF数据服务创建服务并动态创建URI以查询您的实体模型。

30

我知道的唯一另外一种方法是根据您的过滤器构建一个IQueryable。

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

是的,但这是有效的表现明智吗?选择何时执行?到底什么时候ToList()被调用?想象一下,我有非常大的数据集...... – Eduard 2011-04-05 06:38:20

+1

不,这不是性能问题,因为它使用延迟执行来只查询一次。 – BrandonZeider 2011-04-05 12:54:02

+0

+1谢谢你的回答。 – Eduard 2011-04-05 14:05:08

4

您可以使用动态的规范和动态排序。我已经对他们的博客herehere。下面的例子应该可以帮助你 -

 //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)); 

我不知道搜索对象/ DTO你得到,但你可以很容易地创建一个通用搜索对象/ DTO,并可以将其映射到几行GenericSpecification的对象链的代码。过去我曾经使用过WCF服务,它对我来说工作得非常好。