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. 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).
Firstly I tried to create extension method called AllOrEmpty which works fine but not in Linq to entities (it doesn't accept extension methods).
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
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.For instance, your query could be like this:
But I want to mention something regarding your extension method.
The condition
source.All(predicate) || !source.Any()
(hence yourAllOrEmpty
method) does not make any sense because it is equivalent to eithersource.All(predicate)
(yes, this is not a mistake) or!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:This linq query should do what you're trying to do:
If you want clients who has cases where all the history log items has the code set to "WRONG"
If you want to get all the clients who does not have any History log item for any cases.
If you want both conditions together.