2012-03-12 141 views
5

我有以下的LINQ to Entities查詢其中有許多子查詢得到一些總體數據:重構的LINQ to SQL /實體與許多子查詢查詢

var systems = from s in db.Systems 
       orderby s.Name 
       select new SystemSummary 
       { 
        Id = s.Id, 
        Code = s.Code, 
        Name = s.Name, 
        LastException = (
         from a in s.Applications 
         from e in a.Summaries 
         select e.CreationDate 
       ).Max(), 
        TodaysExceptions = (
         from a in s.Applications 
         from e in a.Summaries 
         where e.CreationDate >= today && e.CreationDate < tomorrow 
         select e 
       ).Count(), 
        /* SNIP - 10-15 more subqueries */        
       }; 

我縮短了查詢僅包括子查詢2 ,但可能會有10-15個左右。有沒有一種方法可以重構查詢來清理代碼?我不想看到性能提升。我想通過將子查詢放入單獨的方法來清理代碼,同時仍然確保它是對數據庫的單個調用。這可能嗎?

+0

如果你想要一個更乾淨的代碼,你可能要考慮在你的數據庫 – Mathieu 2012-03-12 17:10:46

+0

@Mathieu創建一個存儲過程,只有這樣,雖然? – Dismissile 2012-03-12 17:18:49

回答

2

我可以提供通過這樣的事情(通過使用您的原始查詢let關鍵字)儘量減少它的長度:

var subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e; 

你也可以有一些refactors這樣的:

subQuery.Count(e=>e.CreationDate >= today && e.CreationDate < tomorrow); 

subQuery.max(e=>e.CreationDate); 

事實上使用點符號並將您的查詢移至相關功能,而不是額外的where子句。

,並使用subQuery在查詢:

  from s in db.Systems 
      orderby s.Name 
      let subQuery = from a in s.Applications 
        from e in a.Summaries 
        select e 
      select new SystemSummary 
      { 
       Id = s.Id, 
       Code = s.Code, 
       Name = s.Name, 
       LastException = subQuery.max(e=>e.CreationDate), 
       TodaysExceptions = subQuery.Count(e=>e.CreationDate >= today 
              && e.CreationDate < tomorrow), 
       /* SNIP - 10-15 more subqueries */        
      }; 

這仍然是一個調用數據庫。

+0

@Dismissile,我錯過了's',我編輯了答案,你可以用'let'在你的查詢中模擬這種方式。 – 2012-03-12 19:55:32

+0

我喜歡這種方法。 – Dismissile 2012-03-12 21:03:33

+0

希望得到這個幫助,也很高興看到你的評論:) – 2012-03-12 21:08:10

0

真的是在你的分隔條件查詢到的多個方法沒有問題。雖然有一些條件。

確保您的查詢IEumerable。這是默認的。

IEnumerable的確保了查詢被存儲在可變的,但不會執行。 編譯器在運行時優化您的查詢。

快速和骯髒爲例:

private MyContext context = new MyContext() 
private IEnumerable<User> getUser(Guid userID) 
{ 
    return context.User.Where(c => c.ID == userID); 
} 

private void evaluateUser() 
{ 
    bool isUserActive getUser().Any(c => c.IsActive) 
} 

你可以看到,查詢有兩種方法。仍然只有一個調用數據庫,因爲IEnumerable存儲查詢而不是結果。該查詢只在需要時執行。

+2

我認爲你的意思是IQueryable,而不是IEnumerable。IEnumerable將使它將查詢轉換爲內存中的對象。 – 2012-03-12 17:39:43

0

你可能要考慮使用let關鍵字創建「變量」本地查詢(這最終結束是你的子查詢)。例如:

var systems = from s in db.Systems 
       orderby s.Name 
       let lastException = (from a in s.Applications from e in a.Summaries select e.CreationDate).Max() 
       ... 

你可以做的可能是創建一個從各種協會的子查詢馬上蝙蝠,以及與這些要素工作的另一種選擇。

var systems = from s in db.Systems 
       orderby s.Name 
       from summaries in 
        (from ta in s.Applications 
        from te in ta.Summaries 
        ... 
        select { APPS = ta, SUMMS = te ,/*anything else you want*/ }) 
       let lastExpire = (from summaries select SUMMS.CreationDate).Max() 

地獄,你甚至可以只留下在第二個例子中放出來,只是使用summaries實體在您的最終選擇。你可能有玩弄有點以確保你沒有得到的任何值的重複,但你可以做一個選擇直接對你summaries至少這樣的重寫,而不是每次你的子查詢。