Ormai ci siamo. Giovedì a Padova sarò impegnato in una sessione su LINQ che riprenderà in qualche modo la serie di post che ho proposto sul mio weblog nelle ultime settimane. Si tratta di una sessione entry-level, come c'è da aspettarsi per una tecnologia così nuova, che però ha l'ambizione di voler eviscerare gli argomenti basilari con lo scopo di far comprendere il funzionamento intimo di LINQ. Cuore della sessione saranno una serie di esempi di codice, alcuni dei quali scritti in diretta. In particolare è mia intenzione guidare i presenti a partire da un semplice ciclo foreach, a costruire una query expression passo passo analizzando i motivi di certe scelte e gli step che il compilatore compie nel fare questa operazione al contrario.

Poi vedremo una serie di esempi di LINQ to Objects, per approdare infine a LINQ to SQL del quale cercherò di dare una dimostrazione ampia che possa aiutare chi non lo conosce a cominciare ad usarlo. Vedremo l'entity designer, l'object tracker e il DataContext. E nel contempo faremo qualche considerazione sull'estensibilità di LINQ.

Non andrò oltre. LINQ è un argomento molto ampio, ma a mio parere è inutile cercare di far stare tutto in un'ora. Una volta presa confidenza con L2O e L2S, cominciare ad usare gli altri dialetti dovrebbe essere un vero gioco da ragazzi...

Vi aspetto perciò tutti. Sarà una bella occasione per una serata in compagnia a discutere di argomenti che sono certo vi interessano sia professionalmente che personalmente.

Appuntamento perciò a Padova il 6 Marzo 2008 presso l'hotel Sheraton alle ore 19:00. Cominceremo cenando assieme e poi... ricchi premi e tanta tecnologia.

Link: http://www.microsoft.com/italy/launch2008/xml/after_padova.htm


Mano a mano che prendo confidenza mi rendo sempre più conto della potenza dello strumento LINQ nella sua declinazione LINQ To Objects. Ecco un esempio pratico da tenere in considerazione:

   1: string text = (from ListItem item in chkLines.Items
   2:                where item.Selected == true
   3:                select item.Text)
   4:                .DefaultIfEmpty()
   5:                .Aggregate((a, b) => a = a + (!string.IsNullOrEmpty(a) ? ", " : "") + b);

Questo esempio in due parole fa:

  1. prende tutte le checkbox selezionate da una CheckBoxlist (where...)
  2. ne estrae il testo (select item.Text)
  3. lo concatena separando gli elementi con una virgola solo quando serve (Aggregate)
  4. gestisce il caso in cui la sequenze sia vuota (DefaultIfEmpty)

L'esempio è banale forse, ma la domanda è: quante righe di codice occorrevano prima per un risultato del genere?

Technorati Tag: ,,

Stanotte ci ho sbattuto la testa per un paio di ore e alla fine mi sono convinto che la serializzazione delle entities di Linq To SQL sostanzialmente... non è possibile! Il mio primo tentativo banale è stato di serializzarle in binario ma immediatamente mi sono reso conto che vengono tutte generate senza l'attributo [Serializable]. Perciò ho provato a scrivere le partial class e metterci l'attributo ma alla fine mi sono dovuto arrendere per il semplice fatto che EntitySet e EntityRef non sono serializzabili a loro volta. A questo punto ho deciso che a me bastava serializzare e quindi anche Xml sarebbe andato bene ma... XmlSerializer non ci riesce, perchè da un errore relativo una circular reference.

Il fatto è che se curiosate nel designer di Visual Studio 2008 scoprirete in fretta una proprietà "Serialization Mode" che può assumere i valori None e Unidirectional. Assegnando Unidirectional la classe generata verrà decorata con gli attributi DataContract e DataMember (e meno male!) e quindi diventerà serializzabile per mezzo del DataContractSerializer o del NetDataContractSerializer. Ecco un esempio di come fare espresso sotto forma di 2 extension methods:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using System.IO;
   6: using System.Runtime.Serialization;
   7: using System.Xml.Serialization;
   8:  
   9: namespace Elite.Linq2SQL
  10: {
  11:     public static class Extensions
  12:     {
  13:         /// <summary>
  14:         /// Serializes the specified obj.
  15:         /// </summary>
  16:         /// <param name="obj">The obj.</param>
  17:         /// <returns></returns>
  18:         public static string ToXml<T>(this T obj)
  19:         {
  20:             using (MemoryStream stream = new MemoryStream())
  21:             {
  22:                 NetDataContractSerializer serializer = new NetDataContractSerializer();
  23:                 serializer.Serialize(stream, obj);
  24:                 return Encoding.UTF8.GetString(stream.ToArray());
  25:             }
  26:         }
  27:  
  28:         /// <summary>
  29:         /// Deserializes the specified data.
  30:         /// </summary>
  31:         /// <typeparam name="T"></typeparam>
  32:         /// <param name="data">The data.</param>
  33:         /// <returns></returns>
  34:         public static T FromXml<T>(this string xml)
  35:         {
  36:             using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
  37:             {
  38:                 NetDataContractSerializer serializer = new NetDataContractSerializer();
  39:                 return (T)serializer.Deserialize(stream);
  40:             }
  41:         }
  42:     }
  43: }

Detto questo può sembrare che bene o male (io propendo per il male...) si sia risolta la questione ma prima di cantare vittoria è opportuno ragionare sul significato di Unidirectional. In breve questo termine sottindente il fatto che la serializzazione comprende esclusivamente l'oggetto radice della gerarchia ed esclude qualunque oggetto che crei un riferimento esterno. Perciò se avete una proprietà collection popolata di oggetti questa non verrà nemmeno presa in considerazione. Il motivo è semplice: così non ci saranno mai circular references.

Quindi tirate le somme si può dire che è meglio togliersi subito dalla testa l'idea di serializzare le entities di Linq To SQL a meno che non vi vogliate impegnare ad implementare ISerializable, che davvero non è la cosa più semplice e immediata che mi venga in mente. Questo, almeno dal mio punto di vista di programmatore ASP.NET significa che le entities non potranno mai essere messe nella ViewState, oppure che se decido inopinatamente di tenerne anche una sola in Session, l'applicazione non potrà mai utilizzare lo State Server di ASP.NET perchè il suo uso implica la serializzazione degli oggetti...

La morale è quindi: occhio a quello che fate!


M'è capitato di sentir parlare dell'entity designer di LINQ come "uno dei tanti inutili designer" di Visual Studio. Non sono un amante dei designer, e quando posso li evito. Ad esempio, per quanto ben fatto sia il designer delle WebForm mi capita di usarlo molto di rado per il semplice fatto che di solito scrivere il codice a mano mi è più rapido.

Ed è proprio questo il punto... il designer deve essere uno strumento che aiuta a risparmiare tempo e non a complicare la vita, perciò mentre un designer come quello per le WebForms ha un utilità relativamente bassa, quello di LINQ è pressochè indispensabile. Per quanto sia possibile scriversi a mano le classi di Linq To SQL chi ci abbia provato si sarà sicuramente reso conto che si tratta di un lavoro improbo e ripetitivo. Non si tratta infatti semplicemente di creare proprietà e metodi, ma anche implementare le interfaccie INotifyPropertyChanged e INotifyPropertyChanging che implicano i dover intervenire sulle proprietà una ad una. Questa implementazione è resa obbligatoria dal fatto che l'object tracking si basa su queste interfacce e senza di esse non sarà in grado di garantire il corretto funzionamento perdendo una delle caratteristiche più importanti di LINQ.

L'uso del designer consente di risparmiare moltissimo tempo e per questo motivo è sicuramente opportuno usarlo.

Technorati Tag: ,

Nei link di oggi di Scott Guthrie c'è ne uno in particolare che ritengo molto utile. Si tratta di un esempio di uso dell'Expression Tree Visualizer, che è stato incluso nell'ultimo aggiornamento degli esempi di CSharp di Visual Studio 2008.

Il tool, che deve essere compilato e installato in Visual Studio (vedere il post originale per le istruzioni), consente di esplorare la struttura di una Expression Tree generata ad esempio in una query LINQ. Si tratta sicuramente di un buon sistema per capire come funzionano questi "strani" oggetti...

Link:


Non sempre si è in grado di scrivere le query LINQ prima di compilare un progetto. Come si può immaginare la possibilità di compilare query composte dinamicamente a runtime è indispensabile. Nel blog di Scott Guthrie è uscito un post che parlava di questo qualche tempo fa, e stamane grazie ad un amico che ne aveva bisogno m'è tornato alla mente. Ecco quindi il link:

Grazie alla libreria (DynamicLibrary.cs) inclusa nei progetti di esempio si possono scrivere cose come la seguente:

   1: Brand brand = (from b in db.Brands
   2:                where b.BrandId == 2
   3:                select b).Single();
   4:  
   5: IQueryable<Car> cars = db.Cars.Where("Brand=@0", brand);
   6:  
   7: foreach (Car car in cars)
   8:     Console.WriteLine(car.Model);

La libreria aggiunge una serie di Extension Methods aggiuntivi che consentono ad esempio di usare una stringa come parametro di una Where...

...Ma non solo... se volete saperne di più, .

Technorati Tag: ,

Le entity class che vengono generate dal designer di Linq to SQL sono definite come partial class. Questo consente di poter scrivere delle classi che completano quelle generate con della logica che non verrà toccata da successive rigenerazioni. Più o meno tutte le classi generate dai tool del framework .NET a partire dalla versione 2.0 sono partial class. xsd.exe, wsdl.exe, etc... e ora naturalmente anche sqlmetal.exe hanno questa utile caratteristica.

Stamane, mentre lavoravo ad uno degli esempi per il prossimo Code Night Launch 2008, ho scoperto una cosa interessante. Se provate ad aggiungere un costruttore ad una delle entity class, nel momento in cui ne fate uso vi troverete con una Null Reference Exception la prima volta che tale entity arriva al DataContext. Spulciando il codice generato si vede chiaramente che il costruttore di default contiene del codice di inizializzazione perciò è obbligatorio chiamarlo nel proprio costruttore:

   1: public partial class Customer
   2: {
   3:     public Customer()
   4:         : this()
   5:     {
   6:         // ... inizializzare qui ...
   7:     }
   8: }

Questo modo non è consueto perchè di solito si fa il contrario assegnando via via i parametri dai costruttori senza parametri in giù. Tuttavia in questo caso è efficate.

Technorati Tag: ,

Dovendo cercare in un array per trovare la stringa più lunga (ma anche la più corta) il codice da scrivere non è molto, ma comunque è abbastanza articolato:

   1: string longest = string.Empty;
   2:  
   3: foreach (string item in stars)
   4:     if (item.Length > longest.Length)
   5:         longest = item;

Quattro righe di codice non sono molte, ma usando LINQ possiamo ottenere il medesimo risultato con una sola riga:

   1: string longest = stars.Aggregate((a, b) => a.Length > b.Length ? a : b);

Il metodo Aggregate durante l'iterazione degli elementi porta con se oltre all'elemento corrente (a) anche quello selezionato dalle precedenti iterazioni (b). Ecco che quindi basta confrontarli e ritornare il più lungo dei due per trovare l'elemento più lungo di tutto l'array. Invertendo il segno > troveremmo il più corto.

Technorati Tag: ,,

In occasione del lancio dei nuovi prodotti della serie 2008, Visual Studio, Sql Server e Windows Server, io e gli altri colleghi di XeDotNet saremo presenti per la nostra "Code Nigh Launch 2008". Si tratta di una serata in compagnia sulle tematiche del Framework 3.5. Ecco l'agenda dell'evento che seguirà il lancio:

Code Night Launch 2008 - 6 Marzo 2008

Sheraton Padova Hotel - Corso Argentina, 5 - Padova PD 35129, Italia

  • ore 19.00 Registrazione
  • ore 19.00 - 19.30 - HAPPY HOUR/DINNER
  • ore 19.30 - 20.00 - Ask the Expert
  • ore 20.00 - 21.00 - IEnumerable Tales: guida pratica per esempi all’uso di LINQ (Andrea Boschin)
  • ore 21.00 – 22.00 - Windows Live, integrazione in applicazioni reali (Davide Vernole)

Nella mia sessione, sulla quale sto lavorando alacremente vedremo assieme LINQ e cercheremo di trovare una chiave di lettura pratica che aiuti a integrarlo velocemente nelle nostre applicazioni. Appuntamento al 6 Marzo 2008.

Registrazione:


Le query expressions di C# 3.0 che di solito si usano per comporre le query di LINQ sono in realtà una facility che permette di ottenere una maggiore leggibilità del codice. Esse infatti vengono tradorre dal compilatore in una sequenza di chiamate ad Extension Methods che poi viene eseguita runtime.

Questo preambolo serve per introdurre in voi il dubbio che le query expressions abbiano qualche limite che possiamo superare facendo uso diretto dei metodi. Un esempio è dato dalla clausola "where" che a prima vista è esaustiva. Un esame più approfondito del relativo metodo Where() ci rivela che quest'ultimo ha due overload, e che solo uno dei due mappa direttamente la query expression. Il secondo metodo accetta come argomento una lambda expression che espone non solo l'elemento ma anche il suo indice nella collection.

Ecco un esempio di quello che possiamo fare:

   1: public static void Main(string[] args)
   2: {
   3:     var r1 = from name in stars
   4:              where name.StartsWith("Gamma")
   5:              select name;
   6:  
   7:     var r2 = stars.Where((name, index) => name.StartsWith("Gamma") && index % 2 == 0);
   8:  
   9:     Display(r1);
  10:     Console.WriteLine("**************************");
  11:     Display(r2);
  12:     Console.ReadLine();
  13: }

La variabile "stars" del mio esempio è un array di stringhe che contiene una serie di nomi di stelle. Per brevità l'ho omessa. Nell'esempio abbiamo che r1 viene valorizzato con una normale query expression mentre per r2 si fa uso del secondo overload di Where(). In questo modo si riesce a selezionare solo gli elementi pari. Si tratta di un esempio semplice, ma rivela che conviene approfondire bene lo strumento perchè ha dei risvolti nascosti che devolo essere rivelati.

Technorati Tag: ,

Poniamo di avere una collection di nostre custom entities che implementa IEnumerable<T> e poniamo di doverla ordinare rispetto ad una proprietà. Se ad esempio la entity rappresenta una riga ordine potremmo dover ordinare le righe per codice prodotto; con LINQ faremmo una cosa del genere:

   1: public void SortRowsBySKU(IEnumerable<OrderRow> rows)
   2: {
   3:     return from item in rows
   4:         orderby item.SKU ascending
   5:         select item;
   6: }

Linq è decisamente vantaggioso, e ci risparmia un bel po' di lavoro. Ma poniamo per un momento di dover ordinare ora la stessa collection per la proprietà Price, e magari dare all'utente la possibilità di scegliere la colonna a runtime. Ovviamente sarebbe una pessima idea creare una query per proprietà. Il codice diventerebbe inutilmente proolisso e poco manutenibile. Per parametrizzare la clausola di orderby potremmo invece usare la reflection come segue:

   1: public void SortRows(IEnumerable<OrderRow> rows, string sortExpression)
   2: {
   3:     return from item in rows
   4:         orderby item.GetPropertyValue(sortExpression) ascending
   5:         select item;
   6: }

Il metodo GetPropertyValue è un ExtensionMethod che viene da una classe di utilità relativa la reflection e semplicemente ottiene il valore di una proprietà di un oggetto passata come argomento di tipo stringa:

   1: public static class ReflectionExtensions
   2: {
   3:     public static object GetPropertyValue(this object value, string propertyName)
   4:     {
   5:         Type type = value.GetType();
   6:         PropertyInfo property = type.GetProperty(propertyName);
   7:         return property.GetValue(value, null);
   8:     }
   9: }

L'esempio in questione probabilmente è poco significativo perchè chiunque di voi avrà da obbiettare che sarebbe meglio fare l'ordinamento direttamente nella istruzione SQL che estrae i valori e io non posso che essere daccordo su questo. Ma sono certo che esistono un buon numero di casi in cui avendo a disposizione un semplice IEnumerable<T> sia necessario ordinarlo per una specifica proprietà. Mi raccomando però, questo esempio si applica solo a LINQ to objects.

Technorati Tag: ,