2016-04-07 20 views
1

有一個結構:實體框架 - 其中任何()和所有()使用的clausule使用

客戶端有多個案件和案件有多個LOGS。

public class Client 
    { 
     public int Id { get; set; } 
     public IEnumerable<Case> Cases { get; set; } 
    } 

    public class Case 
    { 
     public int CaseId { get; set; } 
     public IEnumerable<Log> Histories { get; set; } 
    } 

    public class Log 
    { 
     public int Id { get; set; } 
     public string Code { get; set; } 
    } 

我想檢索客戶端的所有日誌'從每個案例代碼屬性設置爲「錯誤」或根本沒有日誌的客戶端。除此之外,我還有一些簡單的條件,您可以在下面簡化發佈代碼(EF使用IQueryable而不是ICollection)。

首先,我嘗試創建名爲AllOrEmpty的擴展方法,它可以正常工作,但不能在Linq中使用實體(它不接受擴展方法)。

public static class Extensions 
    { 
     public static bool AllOrEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) 
     { 
      return source.All(predicate) || !source.Any(); 
     } 
    } 

     var sampleIds = new List<int>() { 1, 2, 3 }; 

     Entities db = new Entities(); 
     db.Clients 
      .Where(client => client.Cases 
       .Where(cas => sampleIds.Contains(cas.CaseId)) 
       .SelectMany(cas => cas.Histories 
        .Where(log => log.Id < 10) 
        ) 
       .AllOrEmpty(log => log.Code == "WRONG") << ideal solution 
       ) 
       .Select(client => client.Id); 

其次,我試圖在clausule創建具有return語句lambda表達式,它工作正常,但不是IQueryable的和返回錯誤:與語句體lambda表達式不能轉換爲表達式樹

  db.Clients 
      .Where(client => 
      { 
       var logs = client.Cases 
        .Where(cas => sampleIds.Contains(cas.CaseId)) 
        .SelectMany(cas => cas.Histories 
         .Where(log => log.Id < 10) 
         ); 

       return !logs.Any() || logs.All(log => log.Code == "WRONG"); 
      }) 
      .Select(client => client.Id); 

我不知道如何創建這樣的查詢,並保持它簡單,並避免一些髒代碼。任何想法?

+1

可能複製[Entity Framework Filter「Expression >」](http://stackoverflow.com/questions/18337692/entity-framework-filter-expressionfunct-bool) – Igor

回答

0

這LINQ查詢應該做你想做什麼:

var IDs = from client in db.Clients 
       from cas in client.Cases.Where(c => sampleIds.Contains(c.CaseId)) 
       let logs = cas.Histories.Where(l => l.Id < 10) 
       where !logs.Any() || logs.All(l => l.Code == "WRONG") 
       select client.Id; 
0

如果希望客戶端誰擁有,所有的歷史記錄項已代碼設置爲「錯誤」

var clientsWithWrongLogCode = clist.Where(s => s.Cases 
             .Any(c => c.Histories.All(h => h.Code == "WRONG"))); 
情況

如果您想獲取所有沒有任何歷史記錄項目的客戶。

var clientsWithNoLogs = clist.Where(s => s.Cases.Any(c => !c.Histories.Any())); 

如果你想同時使用這兩個條件。

var combined = clist.Where(s => s.Cases.Any(c => c.Histories.All(h => h.Code == "WRONG") 
                    || !c.Histories.Any())); 
+0

你@Shyju和Josh Knack都不理解我。我不想選擇符合條件的案例,但是我會像每個案例中的所有日誌一樣處理,並且只有每個案例中的所有日誌都符合條件(所有代碼都是「WRONG」或者這個包是空的)我想選擇這樣的用戶。 – Lukas

+0

嘗試最後一條LINQ語句(合併)。 – Shyju

1

您可以利用LINQ查詢語法,這與let條款和透明標識符大大簡化了這樣的質疑。

比如你的查詢可以是這樣的:

var query = 
    from client in db.Clients 
    let logs = from cas in client.Cases 
       where sampleIds.Contains(cas.CaseId) 
       from log in cas.Histories 
       where log.Id < 10 
       select log 
    where !logs.Any() || logs.All(log => log.Code == "WRONG") 
    select client.Id; 

但我想提一提關於您的擴展方法的東西。

條件source.All(predicate) || !source.Any()(因此你AllOrEmpty方法)沒有任何意義,因爲它是相當於或者source.All(predicate)(是的,這是不是一個錯誤)或!source.Any(predicate)

,就容易證實,對LINQ到實體通過查看生成的SQL查詢(同一個)和LINQ到對象通過查看Enumerable.Anyreference source或以下簡單的測試:

class Foo 
{ 
    public int Bar { get; set; } 
} 

var source = new List<Foo>(); 
bool test1 = !source.Any() || source.All(e => e.Bar == 0); 
bool test2 = source.All(e => e.Bar == 0); 
bool test3 = !source.Any(e => e.Bar == 0); 
Debug.Assert(test1 == test2 && test2 == test3); 
+0

這很有趣,但你是對的,All()方法對空集合返回true,所以它完全解決了我的問題。謝謝! – Lukas