简体   繁体   English

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

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

There is a structure:有一个结构:

Client have multiple cases and case have multiple 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; }
    }

I want to retrieve clients that have all logs' from each case Code property set to "WRONG" OR clients that have no logs at all.我想从每个案例 Code 属性设置为“错误”或根本没有日志的客户端中检索所有日志的客户端。 Besides I have couple simple conditions that you can see below in simplified for the purposes of publication code (naturally EF uses IQueryable instead of ICollection).除了我有几个简单的条件,你可以在下面看到为了发布代码的目的而简化(自然 EF 使用 IQueryable 而不是 ICollection)。

Firstly I tried to create extension method called AllOrEmpty which works fine but not in Linq to entities (it doesn't accept extension methods).首先,我尝试创建名为 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);

Secondly I was trying to create lambda expression with return statement in where clausule and it works fine but not for IQueryable and returns error: A lambda expression with a statement body cannot be converted to an expression tree其次,我试图在 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);

I have no idea how to create such query and keep it simple and avoid some dirty code.我不知道如何创建这样的查询并保持简单并避免一些脏代码。 Any idea?任何的想法?

You can leverage the LINQ query syntax, which with let clause and transparent identifiers greatly simplifies such queries.您可以利用 LINQ 查询语法,它使用let子句和透明标识符极大地简化了此类查询。

For instance, your query could be like this:例如,您的查询可能是这样的:

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;

But I want to mention something regarding your extension method.但我想提一下有关您的扩展方法的内容。

The condition source.All(predicate) || !source.Any()条件source.All(predicate) || !source.Any() source.All(predicate) || !source.Any() (hence your AllOrEmpty method) does not make any sense because it is equivalent to either source.All(predicate) (yes, this is not a mistake) or !source.Any(predicate) . source.All(predicate) || !source.Any() AllOrEmpty source.All(predicate) || !source.Any() (因此您的AllOrEmpty方法)没有任何意义,因为它等同于source.All(predicate) (是的,这不是错误)或!source.Any(predicate)

You can easily verify that for LINQ to Entities by looking at the generated SQL query (one and the same) and for LINQ to Objects by looking at the Enumerable.Any reference source or the following simple test:您可以通过查看生成的 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);

This linq query should do what you're trying to do:这个 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;

If you want clients who has cases where all the history log items has the code set to "WRONG"如果您希望客户的所有历史日志项的代码都设置为“错误”

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

If you want to get all the clients who does not have any History log item for any cases.如果您想获取任何情况下没有任何历史日志项的所有客户端。

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

If you want both conditions together.如果你想同时满足这两个条件。

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