2015-01-08 61 views
0

最近我遇到了修復如下所示的錯誤的代碼。 這只是很難理解它,並使改變看起來也很危險。 有沒有簡單的方法來分解這種查詢,即德摩根定律或以不同的方式重寫。簡化多條件下的複雜linq查詢

SomeReturnType xyz() 
{ 
    return (from m in this.context.tblMessages 
      where m.SystemActionID.HasValue && 
      (userID == null || m.RecipientID == null || m.RecipientID == userID) && 
      m.TargetUserType == userType && 
      (
       (territoryID.HasValue && !m.TerritoryID.HasValue) || 
       (!territoryID.HasValue && !m.TerritoryID.HasValue) || 
       (territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value) 
      ) && 
      (
        (regionID.HasValue && !m.RegionID.HasValue) || 
        (!regionID.HasValue && !m.RegionID.HasValue) || 
        (regionID.HasValue && m.RegionID.HasValue && regionID.Value == m.RegionID.Value) 
      ) && 
      (
        (teamID.HasValue && !m.TeamID.HasValue) || 
        (!teamID.HasValue && !m.TeamID.HasValue) || 
        (teamID.HasValue && m.TeamID.HasValue && teamID.Value == m.TeamID.Value) 
      ) && 
      m.SystemActionData == additionalData && 
      (!completed.HasValue || m.IsSystemActionCompleted == completed.Value) 
      select m).FirstOrDefault(); 
} //function xyz ends 

回答

1

我在這種情況下使用IQueryable。我已經使用了一些虛擬名稱而不是您的示例來演示想法本身 - 您需要逐步應用「where」子句。

IQueryable<Account> query1 = from account in storage.Accounts 
           where account.Username == username 
           select account; 

IQueryable<SomeNewTypeIfNecessary> query2 = from account in query1 
              where account.ID > 100 
              select new SomeNewTypeIfNecessary { ID = account.ID }; 

// Final call doing real query to database. 
List<SomeNewTypeIfNecessary> accounts = query2.ToList(); 

此外,這種技術允許動態地添加(主要是其中和的OrderBy條款),以獲得依賴於某些用戶選擇(例如排序方向)的特定數據所需的語句。

+0

LINQ * is * using IQueryable。 –

+0

@PanagiotisKanavos:你究竟是什麼意思?我告訴我需要IQueryable來存儲中間結果(一些表達式樹),而不是在同一個語句中執行查詢。請在任何評論前仔細閱讀來源。 – Maxim

+1

沒有中間結果。 IQueryable是查詢的抽象,而不是查詢的結果。你正在做的是*組成一個查詢。實際上,您可以將代碼重寫爲query2 = query1.Where(account => account.ID> 100); query3 = query2.Select(...)'和結果將完全相同。當你調用'ToList()'時,最終只有一個查詢會被髮送到服務器。 –

3

首先,這段代碼看起來像是試圖將錯誤的SQL代碼移動到LINQ:這些語句包含參數檢查,試圖創建一個可以具有「可選」參數的查詢。看起來作者試圖將存儲過程移到代碼中。

這兩件事都是非常糟糕的想法。

  • 首先,SQL Server將根據其參數緩存查詢的執行計劃。這意味着該過程的第一次執行將確定所有稍後調用的計劃。 「可選」參數通常會導致非最佳計劃(例如,通過省略索引)
  • 存儲過程是隱藏複雜查詢的抽象層,允許您使用產品的完整SQL語法。將它們移動到代碼只會讓事情變得更難,而不是更容易。
  • 最後,LINQ不需要「可選」參數。您可以編寫查詢,並將IQueryable轉換爲IEnumerable時生成最終的SQL語句。在這種情況下,當您調用FirstOrDefault時。

例如,你可以改變這一點:

(from m in this.context.tblMessages 
     where m.SystemActionID.HasValue && 
     (userID == null || m.RecipientID == null || m.RecipientID == userID) && 
     m.TargetUserType == userType && 
     (
      (territoryID.HasValue && !m.TerritoryID.HasValue) || 
      (!territoryID.HasValue && !m.TerritoryID.HasValue) || 
      (territoryID.HasValue && m.TerritoryID.HasValue && territoryID.Value == m.TerritoryID.Value) 
     ) && 

這個

if (userId==null) 
    return null; 
var query=from m in this.context.tblMessages 
     where m.SystemActionID != null 
       && (m.RecipientID == null || m.RecipientID == userID) 
       && m.TargetUserType == userType ; 
if (territoryID.HasValue) 
    query=query.Where(m=>m.TerritoryId==null || m.TerritoryId==teritoryID); 

可以簡化以同樣的方式在語句的其餘部分,例如:

if (regionID.HasValue) 
    query=query.Where(m=>m.RegionId==null || m.RegionId==regionID); 
if (teamID.HasValue) 
    query=query.Where(m=>m.TeamID==null || m.TeamID==teamID); 

LINQ查詢不應該將參數與常量進行比較 - 爲什麼對於讓服務器做一個常量檢查,你可以很容易地在客戶端上做?

這將導致查詢更簡單,更容易優化服務器