[英]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.