1.LINQ Language INtegrated Query Giovanni Lagorio
[email protected] JUG Genova, 14 Luglio 2009 2. Licenza Questi lucidi sono rilasciati sotto la licenza Creative Commons Attribuzione-Non commerciale-Non opere derivate 3.0 Unported. Per leggere una copia della licenza visita il sito web http://creativecommons.org/licenses/by-nc-nd/3.0/ o spedisci una lettera a Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. In due parole, possono essere liberamente usati, copiati edistribuiti purché: 1. Venga citata la fonte originale 2. Non vengano usati in ambito commerciale 3. Non vengano modificati in nessun modo 3. Cosa vedremo? • LINQ – Cos’è? Requisiti, pubblicità – Accenni di cosa succede “dietro le quinte”• LINQ to Objects• LINQ to SQL • Panoramica di ADO Entity Framework • Conclusioni 4. Cos’è LINQ? Uno strumento per interrogare in modouniforme – Oggetti in memoria (LINQ to Objects) – Database relazionali (LINQ to SQL) – Documenti XML (LINQ to XML) – Modelli E/R (LINQ to Entities) – ...altro... LINQ to Flickr, LINQ to whatever LINQ non è ORM, ma è un’ottima interfaccia per interrogarli 5. Cos’ha di tanto speciale LINQ? • Uniformità ed Estensibilità • Query a livello di linguaggio – Type checking – Nell’IDE:• Intellisense• Refactoring – Non serve escaping niente più (SQL) injection 6. Cosa serve? • .NET 3.5/Visual Studio 2008– C# 3 C# 2 + “quello che serve per LINQ”– il risultato è compatibile con .NET 2.0 • Quindi, tutta “compiler magic”– type inference, quanto basta, e– zucchero sintattico come se piovesse • Quindi, è realistico che arrivi in Java– Java 6 = Java 5 C# 2– Per Java 7 si parla di closure... 7. LINQ to Objects string[] strings = { "g", null, "gENoVa", "jug" };var query = from s in strings where s.Length>2 orderby s descending select s.ToUpper();var query2 = from s in querywhere s.StartsWith("G")select s.Length;strings[1] = "a";Sum=6 int sum = query2.Sum();Console.WriteLine("Sum={0}n", sum);JUG foreach (string s in query) Console.WriteLine(s);GENOVA 8. Var • Solo per dichiarare variabili locali, il tipo (inferito) è quello dell’inizializzatore • Esempio: int i = 0; var i = 0; // equivalenti, finché non cambia inizializz. • E’ chiaramente molto comoda con i generici... List lls =new List(); var lls = new List(); • Non solo per i pigri, essenziale in alcuni casi 9. LINQ to SQL using (var context = new JUGDataContext()) {var q = from user in context.Userswhere user.lastname.Length>1select user;q = from user in q orderby user.lastname select user;foreach (var x in q) // esegue una DB queryConsole.WriteLine(x.lastname + " " +x.firstname); } 10. ToList/ToArray var q = from user in context.Usersorderby user.lastnameselect user; // esegue 10 volte la query for (int a = 0; a < 10; ++a)foreach (var x in q) // ...List l = q.ToList(); // esegue una sola volta sul DB for (int a = 0; a < 10; ++a)foreach (var x in l) // ... 11. Proiezioni e Tipi anonimi var q = from user in context.Users orderby user.lastname select new { user.firstname, user.lastname, fullName = user.firstname+" "+user.lastname } ; foreach (var x in q)Console.WriteLine("nome={0}, cognome={1}, nomecompleto={2}", x.firstname, x.lastname, x.fullName);Qui var diventa obbligatorio! Che tipo ha x? Un tipo anonimo, sappiamo solo che ha un campo firstname di tipo string, ecc. 12. Scorciatoie varie... var array = new[] { "ciao", "mondo" }; var anA = new A(3) { G=3 }; var anotherA = new A { F=5, G=3 }; var something = new { Answer = 42 };Nei primi tre casi, al posto di var avreipotuto usare: string [], A e A 13. Esempio con groupby string[] strings = { "Gio", "Java", "Genova", "JUG", "Zorro" };var query = from s in strings group s by s[0] into sameFirstLGroup orderby sameFirstLGroup.Count() ascending FirstLetter = Z select new { FirstLetter = sameFirstLGroup.Key, Words = Words = sameFirstLGroup }; Zorro FirstLetter = G foreach (var g in query) {Words =Console.WriteLine("FirstLetter = {0}",Gio g.FirstLetter);GenovaConsole.WriteLine("Words = "); FirstLetter = Jforeach (var w in g.Words) Words = Console.WriteLine("t{0}", w); Java }JUG 14. Altro zucchero sintattico... var ls = new List() { "qui", "quo", "qua" }; var d = new Dictionary(){ { 1, "pippo" }, { 2, "pluto" } };Auto-implemented properties:int pippo { get; set; } 15. Cos’è davvero una query? • Qualcosa che “trasforma” una sequenza in un’altra – string[] strings = { "qui", "quo", "qua" }; var q = from s in strings select s.Length; • Ok…cos’è una sequenza? – tutte le “robe foreach-abili”: IEnumerable 16. Sequenze di “cosi” • IEnumerable • java.lang.Iterable– IEnumerator – IteratorGetEnumerator()iterator() • IEnumerator • java.lang.Iterator– bool MoveNext()– hasNext()– T Current { get; } – next()– (... Reset e Dispose che non ci– (... e remove che non ciinteressano) interessa) ... 17. Esempi Se il risultato è una sequenza di oggetti di tipo R, il tipo della query sarà IEnumerable • string[] strings = {"qui", "quo", "qua"}; IEnumerable q1 = from s in strings select s.Length; • IEnumerable q2 = from s in strings select s; • IEnumerable q3 = strings; • IEnumerable q4 = from i in (from s in strings select s.Length) select i > 0; 18. Troppa magia... come funziona?!? var q = from user in /* un certo IEnumerable */ where user.firstname == "Giovanni" select user; foreach (var x in q)Console.WriteLine(x.firstname);Come può generare il codice corretto?!? La condizione deve diventare del codice .NET, una clausola WHERE in SQL o che altro? Dipende dal provider... che non conosciamo! 19. C’è il trucco ;-) 1. Esiste una traduzione che trasforma le query LINQin una serie di chiamate di metodo – Traduzione puramente sintattica: il risultato viene poi compilato con le regole usuali 2. La sorgente può essere più specifica di quantodetto prima, per esempio:interface IQueryable : IEnumerable, ...rappresenta, terminologia mia, le sequenze“LINQ-aware” 20. Versione tradotta,senza “zucchero sintattico” var q = /* ... un IEnumerable/IQueryable ... */ .Where(user => user.firstname == "Giovanni") .Select(user => user);Ora sì che va meglio! Al posto di un problema ne abbiamo due 1. Cosa sono Where e Select? Sembrano metodi manon fanno parte di IEnumerable... 2. Cosa gli passiamo? 21. Extension methods • E’ possibile aggiungere metodi (implementati) a una qualunque classe/interfaccia • Visti dall’intellisense (quasi) come gli altri • Meccanismo molto potente, ricorda l’Aspect Oriented Programming – sembra fin troppo facile abusarne 22. Extension methods: come? • Si usa this come modificatore del primo parametro di un metodo statico public static int m(this T x, int k, ...) • Il compilatore trasforma le chiamate expr.m(1, 2, 3) dove expr ha tipo T (e non c’è nessun metodo migliore) in: MiaClasseStatica.MioExtMethod(expr, 1, 2, 3) 23. Esempio di estensione (totalmente inutile) public static class MyStringExtension { public static int TwiceLength(this string s) {return s.Length * 2; } }... Console.WriteLine("ciao".TwiceLength());// stampa 8, trasformata dal compilatore in: Console.WriteLine(MyStringExtension. TwiceLength("ciao")); 24. Lambda Expressions (Closures) • Fanno finta di essere λ-astrazioni del λ-calcolo , ovvero definizioni di funzioni “inline” Esempi:– (int x, int y) => x + yrappresenta la funzione che somma x e y– a => a * arappresenta la funzione quadrato– (x, y) => { if (x < y) return x; else return y; }rappresenta la funzione che restituisce il minimo • Il tipo dei parametri può essere inferito– Se il parametro è uno solo, non servono le parentesi 25. Lambda Expression (cont.) Una lambda-expression è, di per sé, totalmente inutile (!) ma può essere convertita in: • un delegate, in sostanza, in codice .NET che valuta la funzione, oppure in • un Expression tree, (codice .NET che costruisce) una struttura dati che rappresenta l’espressione, che il provider trasformerà in una qualche forma più furba per l’esecuzione (per esempio, una query SQL per un DB relazionale) 26. Esempio expression tree 27. Riprendiamo la versione “dietetica” var q = /* ... */ .Where(user => user.firstname == "Giovanni") .Select(user => user);In base al tipo della sorgente: public static IEnumerable Where( this IEnumerable source, Func predicate) public static IQueryable Where(this IQueryable source,Expression predicate) 28. Ma allora, cosa devo usare? IEnumerable IQueryableDipende, comunque un IEnumerable siconverte facilmente con asQueryableNon è (sempre) un cast: int[] ints = { 1, 2, 3 }; IQueryable iq = ints.AsQueryable(); 29. Limiti dell’approccio • Alcune espressioni possibili solo con LINQ to Objects, per esempio in LINQ to SQL: var q = from user in context.Users where user.MyMethod()>0 /* boom */ select user.MyMethod() /* ok */ ;– Comunque, molti (tutti?) metodi standard dinumeri/stringhe/date vengono tradotti • Mi sembra sconsigliabile, ma limitandosi a LINQ to Objects è possibile modificare gli oggetti visitati 30. ADO ENTITIES FRAMEWORK 31. ADO Entities Framework • E’ un ORM, al di là di quello che sostiene M$ • Permette di lavorare a livello di Modello E/R (ereditarietà, relazioni molti-a-molti, ...) • Si può interrogare via: – Entities SQL (SQL), per esempio: SELECT VALUE c FROM E.Contacts AS c WHERE c.FirstName='Pippo' – LINQ to Entities La sintassi è quella che vi aspettate 32. Mapping • Descritto da tre file XML – Ma VS li nasconde abbastanza ModelloStoreConcettuale Mapping Schema (E/R) .MSL Mapping.SSDL.CSDLSpecificationLanguage Store Schema Definition Conceptual Schema Language Definition Language(tipicamente)DBOggetti Relazionale 33. Esempio: Entities vs DB Schema 34. EntityContext Dai file XML otteniamo: • Le classi (parziali) corrispondenti ai tipi entità • Una classe QualcosaEntityContext (che estende ObjectContext) che ci permette di dialogare con il modello, tramite:– proprietà che corrispondono agli insiemi entità– metodi AddTo Il contesto materializza gli oggetti e traccia icambiamenti, salvati con SaveChanges 35. Esempio di inserimento dati using (var context = new JUGEntities()) {User u = new User() { username = "Gio",firstname="g", lastname="l" };Role r = new Role() { rolename = "Admin" };u.Roles.Add(r);Document d = new ToDo() { Author = u,title = "demo", what="boh" };context.AddToDocuments(d);context.SaveChanges();// tutti gli oggetti vengono salvati e// l'ordine delle insert e` automatico } 36. Esempio di interrogazione using (var context = new JUGEntities()) {var q = from d in context.Documents.Include("Author.Roles")where d.Author.username=="Gio"select d;var aDoc = q.First();//aDoc.AuthorReference.Load();User author = aDoc.Author;Console.WriteLine("Author: {0}nRoles:", author.firstname);//author.Roles.Load();foreach (Role r in author.Roles) Console.WriteLine("t{0}", r.rolename); } 37. Esempio di modifica/cancellazioneusing (var context = new JUGEntities()) {var todos = (from d in context.Documents.OfType()select d).ToArray();context.DeleteObject(todos[0]);todos[1].what = "blabla";context.SaveChanges(); } 38. LINQ to SQL vs LINQ to Entities Visto che, alla fin fine, si parla di DB relazionali, un confronto è inevitabile.Alcune domande abbastanza ovvie sono: • LINQ to SQL è un ORM? • Quanto si assomigliano? • Posso passare dall’uno all’altro con facilità? 39. LINQ to... SQLEntities E’ un ORMNo Sì Cosa si “vede”?Delle tabelle, relazioni 1-n Entità e Relazioni (anche n-m)Ereditarietà No (sì ma...)Sì TransazioniNo (ma basta wrappare in Si (su DB singolo, se non basta:ScopeTransaction)ScopeTransaction) Query SQL generate RagionevoliA volte ragionevoli Da modello a DB eNo (ma avrebbe senso?) Solo da VS 2010, purtroppo viceversa? Sincronizzazione No (mi sembra, VS2010?), Sì schema DB? non grave: classi parziali Editor Visuale Sì Sì al 90% (speriamo in VS 2010...) Lazy/eager loading Per context/implicitoPer query/esplicitoLocking ottimisticoDi default su tuttoSì, ma di default su nullaSingle/SingleOrDefault Sì No! (Eccezione a runtime) 40. Conclusioni • Oggi: LINQ to – Objects: molto comodo – XML: esiste, non l’ho mai usato – SQL: ok, ma alle “ORMeggia” a sproposito – Entities: interessante, probabilmente immaturo • Domani? – LINQ in Java? – LINQ to (N)Hibernate: abbastanza non-m$ per essere apprezzato da tutti? – PLINQ, un .AsParallel() rende la query parallela! 41. Grazie per l’attenzione ...domande?