实体框架 - 使用Any()和All()的clausule

时间:2016-04-07 16:47:45

标签: c# entity-framework linq lambda

有一个结构:

客户端有多个案例,案例有多个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; }
    }

我想从每个案例中检索具有所有日志的客户端Code属性设置为“WRONG”或者根本没有日志的客户端。此外,我有几个简单的条件,你可以在下面简单地看到出版代码的目的(当然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);

其次我试图使用return语句创建lambda表达式,其中clausule和它工作正常但不适用于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);

我不知道如何创建这样的查询并保持简单并避免一些脏代码。有什么想法吗?

2 个答案:

答案 0 :(得分: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)(是的,这不是错误)或{{1} }。

通过查看生成的SQL查询(同一个)和LINQ to Objects,您可以通过查看!source.Any(predicate) reference source或以下简单测试轻松验证LINQ to Entities:

Enumerable.Any

答案 1 :(得分: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()) );