2011-04-04 69 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服務,它對我來說工作得非常好。