Si consideri il seguente query LINQ to Entities:Utilizzando Dove (Expression <Func <T, bool>>) in IGrouping
return (from lead in db.Leads
join postcodeEnProvincie in postcodeEnProvincies
on lead.Postcode equals postcodeEnProvincie.Postcode
where (lead.CreationDate >= range.StartDate) && (lead.CreationDate <= range.EndDate)
group lead by postcodeEnProvincie.Provincie into g
select new Web.Models.GroupedLeads() {
GroupName = g.Key,
HotLeads = g.Count(l => l.Type == Data.LeadType.Hot),
Leads = g.Count(),
PriorityLeads = g.Count(l => l.Type == Data.LeadType.Priority),
Sales = g.Count(l => l.Sold),
ProductA = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productA", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productA", StringComparison.CurrentCultureIgnoreCase))))),
ProductB = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productB", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productB", StringComparison.CurrentCultureIgnoreCase))))),
ProductC = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productC", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productC", StringComparison.CurrentCultureIgnoreCase))))),
ProductC = g.Count(l => l.Producten.Any(a => ((a.Name.Equals("productD", StringComparison.CurrentCultureIgnoreCase)) || (a.Parent.Name.Equals("productD", StringComparison.CurrentCultureIgnoreCase)))))
}).ToList();
Se siete come me, le dita dei piedi arricciarsi la ripetizione della logica di selezione del prodotto. Questo schema si ripete anche in un altro punto. Ho prima tentato di sostituirlo con un metodo di estensione su IEnumerable, che ovviamente non funziona: Linq to Entities ha bisogno di un'espressione per analizzare e tradurre.
così ho creato questo metodo:
public static System.Linq.Expressions.Expression<Func<Data.Lead, bool>> ContainingProductEx(string productName)
{
var ignoreCase = StringComparison.CurrentCultureIgnoreCase;
return (Data.Lead lead) =>
lead.Producten.Any(
(product =>
product.Name.Equals(productName, ignoreCase) ||
product.Parent.Name.Equals(productName, ignoreCase)
));
}
La seguente selezione ora funziona perfettamente bene:
var test = db.Leads.Where(Extensions.ContainingProductEx("productA")).ToList();
Tuttavia, questo non si compila, perché IGrouping non contiene un override di Dove che accetta un espressione:
return (from lead in db.Leads
join postcodeEnProvincie in postcodeEnProvincies
on lead.Postcode equals postcodeEnProvincie.Postcode
where (lead.CreationDate >= range.StartDate) && (lead.CreationDate <= range.EndDate)
group lead by postcodeEnProvincie.Provincie into g
select new Web.Models.GroupedLeads()
{
GroupName = g.Key,
HotLeads = g
.Where(l => l.Type == Data.LeadType.Hot)
.Count(),
Leads = g.Count(),
PriorityLeads = g
.Where(l => l.Type == Data.LeadType.Priority)
.Count(),
Sales = g
.Where(l => l.Sold)
.Count(),
ProductA = g
.Where(Extensions.ContainingProductEx("productA"))
.Count(),
ProductB = g
.Where(Extensions.ContainingProductEx("productB"))
.Count(),
ProductC = g
.Where(Extensions.ContainingProductEx("productC"))
.Count(),
ProductD = g
.Where(Extensions.ContainingProductEx("productD"))
.Count()
}).ToList();
Casting g di IQueryable compila, ma poi produce un "errore di provider di dati .NET Framework interno 1025.".
C'è un modo per avvolgere questa logica nel proprio metodo?
LINQ to EF è un abstracti su SQL. Se non puoi scrivere ciò che vuoi in SQL, non importa ciò che EF fa. In realtà, è molto più semplice scrivere query complesse in SQL (ad es. Usando viste e UDF) piuttosto che cercare di approssimare lo stesso in LINQ. Ad esempio, quante query vengono eseguite per quello che pensi sia una singola query LINQ? Usa SQL Profiler per vedere cosa sta succedendo. BTW, A 'Where()' dopo un 'Group' è l'equivalente di' HAVING' in T-SQL. Solo gli aggregati sono consentiti nella clausola 'HAVING' –
L'espressione di query Linq iniziale viene compilata correttamente con SQL. I miei tentativi successivi stanno solo cercando di non ripetere più e più volte le stesse espressioni.Il problema esiste solo nell'astrazione, non in ciò che astrae. –
L'originale può essere compilato ma sarà SQL complesso e molto lento. Ad ogni modo, il problema è l'astrazione che perde. EF tradurrà l'intero albero delle espressioni in SQL, quindi il tuo metodo restituirà un albero Expression diverso dall'originale e confonderà EF. I confronti tra stringhe BTW in SQL * non * fanno distinzione tra maiuscole e minuscole, quindi è necessario rimuovere le chiamate a 'String.Equals' - rendono il codice difficile da leggere e inducono l'utente a pensare che abbiano qualche effetto. Inoltre, da dove proviene 'Parent'? Il modo in cui appare all'interno di "Any", potrebbe costringere altre query all'interno dei tuoi aggregati. –