Questo post nasce da un fatto strano che ho notato sviluppando un progetto con Entity Framework, considerate il seguente spezzone di codice.
1 using (NorthwindEntities context = new NorthwindEntities())
2 {
3 IEnumerable<Customers> customers1 =
4 context.Customers
5 .Where(c => c.CustomerID.Contains(""));
6 Console.WriteLine(customers1.Count());
7
8 IEnumerable<Customers> customers2 =
9 context.Customers
10 .ToList()
11 .Where(c => c.CustomerID.Contains(""));
12 Console.WriteLine(customers2.Count());
13 }
Il codice è veramente banale, nel primo caso si effettua una query LINQ to entities chiedendo tutti i clienti il cui id contiene stringa nulla, nel secondo caso si effettua un'operazione molto simile, si caricano in memoria tutti i clienti (perchè nella linea 10 il ToList() essendo un not-deferred operator effettua la query nel database e carica gli oggetti in memoria) e poi si effettua un filtro sugli oggetti in memoria con la stessa condizione precedente, ovvero filtrare tutti i clienti il cui id contiene stringa nulla.
Il risultato di questo snippet è
0
91
ovvero filtrando sul database si ottengono zero oggetti mentre filtrando gli oggetti in memoria otteniamo 91 oggetti. Naturalmente il secondo risultato è quello corretto, dato che ogni stringa contiene logicamente la stringa nulla e quindi mi aspetto che vengano tornati tutti i clienti che non hanno un CustomerID nullo. Questo problema deriva da una pagina asp.net che deve soddifare queste spefiche. E' necessario mostrare tutti i clienti che contengono nel CustomerId una stringa digitata dall'utente con la condizione che se l'utente non digita nulla vengano tornati tutti i clienti.
Ora, senza entity framework basta fare una query sql con una condizione del tipo CustomerId like '%' + stringaricerca + '%' ed ottenere il comportamento previsto; EF invece traduce la query nel modo seguente
SELECT
[Extent1].[Address] AS [Address],
[Extent1].[Checked] AS [Checked],
...
FROM [dbo].[Customers] AS [Extent1]
WHERE (CAST(CHARINDEX(N'', [Extent1].[CustomerID]) AS int)) > 0
per il contains viene utilizzata la funzione CHARINDEX che purtroppo se si passa una stringa nulla fallisce sempre. Questo significa che con Entity Framework le query sul modello concettuale non mappano fedelmente le query fatte su oggetti in memoria. Dato che EF dovrebbe costituire un layer (repository) per il quale io "lavoro" solo su oggetti mentre EF si occupa di gestire le query da fare al database, mi attendo sempre che il comportamento di una query linq to entity produca gli stessi risultati della corrispondente linq to object fatta su oggetti in memoria. Sebbene questo possa sembrare un peccato veniale, dal punto di vista concettuale è fondamentale che questo requisito sia mantenuto.
Alk.