簡體   English   中英

實體框架 - 使用 Any() 和 All() 的 clausule

[英]Entity Framework - where clausule with Any() and All() use

有一個結構:

客戶有多個案例,案例有多個日志。

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

我想從每個案例 Code 屬性設置為“錯誤”或根本沒有日志的客戶端中檢索所有日志的客戶端。 除了我有幾個簡單的條件,你可以在下面看到為了發布代碼的目的而簡化(自然 EF 使用 IQueryable 而不是 ICollection)。

首先,我嘗試創建名為 AllOrEmpty 的擴展方法,它工作正常,但在 Linq to entity 中不起作用(它不接受擴展方法)。

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

其次,我試圖在 where 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);

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

您可以利用 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() source.All(predicate) || !source.Any() AllOrEmpty source.All(predicate) || !source.Any() (因此您的AllOrEmpty方法)沒有任何意義,因為它等同於source.All(predicate) (是的,這不是錯誤)或!source.Any(predicate)

您可以通過查看生成的 SQL 查詢(一個和相同的)輕松驗證 LINQ to Entities 和 LINQ to Objects 通過查看Enumerable.Any 參考源或以下簡單測試:

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

這個 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;

如果您希望客戶的所有歷史日志項的代碼都設置為“錯誤”

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM