Chi arriva a Entity Framework partendo da Linq To SQL probabilmente dopo aver apprezzato molte migliorie come ad esempio il modello più vicino ad un ObjectModel quale ci si aspetta, probabilmente cadrà in un fase “depressiva” perchè si rende conto che oltre ad aver guadagnato qualcosa, nel contempo ha anche perso molto.

Non sto ad elencarvi questi casi, ma piuttosto mi vorrei concentrare su quello che mi è capitato oggi e che mi ha fatto perdere un bel po’ di tempo. In buona sostanza per Entity Framework esistono delle stored procedure “regolari” e altre per così dire “fuori ordinanza”. Se voi avete una stored procedure che vi restituisce un resultset sovrapponibile con una delle entity del vostro dominio allora non c’è problema, siamo nel campo delle procedure ammesse. Ma se per caso come oggi mi è capitato dovete fare una procedura che vi restituisce uno scalare o anche nulla, allora non c’è niente da fare... il designer di Entity Framework si rifiuta di prenderla in considerazione. O meglio: fa finta di fare il suo dovere ma poi vi molla sul più bello e cioè quando c'è da generare il codice che mappa la procedura nell'Object Context.

Per carità, se mettiamo sul piatto della bilancia i vantaggi rispetto agli svantaggi ce ne a sufficienza per prendere in considerazione EF, ma di fronte a queste cose verrebbe voglia di...

Comunque ecco come fare per gestire questo tipo di evenienze. Innanzitutto dovete creare la partial class che si sovrappone al vostro EDMX.

   1: public partial class MyEntities
   2: {
   3: }

Così facendo possiamo ora aggiungere la nostra procedura mappando i parametri su una stored procedure. Poniamo ad esempio di dover chiamare una sp denominata sp_Message_Post. Passiamo al designer e come di consueto mappiamo la procedura ricavandola dal Database con "Update model from database". Una volta che esce la lista delle procedure nel tab "Add" mettiamo la spunta sulla procedura e confermiamo l'aggiornamento.

A questo punto dobbiamo creare il "function import". Si tratta in buona sostanza dell'operazione con cui di informa il designer che una certa procedura va mappata su un metodo dell'ObjectContext e soprattuto che essa avrà un certo tipo di valore di ritorno. Nel Model browser si seleziona la procedure e con il tasto destro si sceglie "Create function Import". Diamo un nome a questa funzione, ad esempio "PostMessage" e un tipo di valore di ritorno, nel nostro caso nessuno.

Ed è qui che accade la cosa sconcertante. Se specificate che la vostra procedura ha un valore di ritorno scalare o non ce l'ha proprio il designer si considera sollevato dell'onere di creare il metodo nell'ObjectContext mentre solo nel caso in cui il valore sia una Entity allora il designer di degna di fare il proprio lavoro come si deve.

Allora ecco il codice da scrivere per immettere un metodo che wrappa il function import appena creato:

   1: public void PostMessage(
   2:     int companyId,
   3:     byte messageType,
   4:     byte priority,
   5:     byte deviceType,
   6:     string recipient,
   7:     string sender,
   8:     DateTime dateCreated,
   9:     byte[] messageBinary)
  10: {
  11:     EntityParameter companyIdParameter = 
  12:         new EntityParameter("companyId", DbType.Int32);
  13:     companyIdParameter.Value = companyId;
  14:     
  15:     // e così via per tutti i parametri...
  16:  
  17:     this.ExecuteFunction(
  18:         "PostMessage", 
  19:         companyIdParameter, 
  20:         messageTypeParameter, 
  21:         priorityParameter, 
  22:         deviceTypeParameter, 
  23:         recipientParameter, 
  24:         senderParameter, 
  25:         dateCreatedParameter, 
  26:         messageBinaryParameter);
  27: }

Potrebbe sembrare tutto, ma manca ancora un pezzetto. Bisogna infatti create una versione nuova del metodo ExecuteFunction che crei il command e faccia la chiamata, non alla procedura in questione ma al function import. Ecco il codice:

   1: protected void ExecuteFunction(string functionName, params EntityParameter [] parameters)
   2: {
   3:     DbCommand command = this.Connection.CreateCommand();
   4:     command.CommandType = CommandType.StoredProcedure;
   5:     command.CommandText = this.DefaultContainerName + '.' + functionName;
   6:  
   7:     foreach (EntityParameter p in parameters)
   8:         command.Parameters.Add(p);
   9:  
  10:     if (command.Connection.State == ConnectionState.Closed)
  11:         command.Connection.Open();
  12:  
  13:     try
  14:     {
  15:         command.ExecuteNonQuery();
  16:     }
  17:     finally
  18:     {
  19:         command.Connection.Close();
  20:     }
  21: }

In buona sostanza dobbiamo tornare ad "abbassarci" a gestire in proprio la connessione. Creiamo il command, assegnamo il nome del function import prependendo il "DefaultContainerName" cioè il namespace in cui vengono generati gli oggetti. In seguito aggiungiamo i parametri al comando e poi eseguiamo il tutto prendendoci l'onere di aprire la connessione se non lo è già e di chiuderla non appena terminato.

Tutto sommato è un po' di codice cui sicuramente non siamo nuovi. Ma la domanda è: perchè il designer non si assume questo onere per noi?

Technorati Tag:

Non so ancora esattamente a cosa mi potra' servire, ma esplorando l'entity framework ho scoperto che e' possibile generare le classi sulle quali sono mappate le entita' con poche righe di codice. Ho fatto la scoperta andando a disassemblare con Reflector il tool edmgen.exe che di solito viene usato per generare le entita' a partire dal modello concettuale, ovvero dal file con estensione CSDL.

Una volta che abbiamo aggiunto un riferimento a System.Data.Entity.Design possiamo accedere ad una classe EntityClassGenerator che con poche customizzazioni e' in grado di generare appunto il codice delle suddette classi. Ecco come:

   1: class Program
   2: {
   3:     static void Main(string[] args)
   4:     {
   5:         LanguageOption options = LanguageOption.GenerateCSharpCode;
   6:  
   7:         EntityClassGenerator generator = new EntityClassGenerator(options);
   8:  
   9:         generator.GenerateCode(@"C:\Files\Entities.csdl", @"C:\Files\Generated.cs");
  10:     }
  11: }

Il metodo Generate() dispone anche di altri overload che consentono ad esempio di generare il file di destinazione in memoria per poi magari inserirlo in un assembly da compilare.

Come gia' detto al momento non ne vedo una utilita' pratica ma suppongo che pensandoci bene una la possiamo anche trovare. E se non fosse cosi' perlomeno qualcosa di nuovo lo ho imparato...


Una delle cose che capitano piu' spesso realizzando l'interfaccia di un software e' la necessita' di estrarre delle coppie nome-valore per popolare oggetti quali le DropDownList, ma anche CheckBoxList e altri tipi di controllo. Quello che succede spesso e' che ci vediamo costretti a scrivere delle query ad-hoc, magari piu' di una per ogni tabella in modo tale da ottenere solo ed esclusivamente i due campi che ci servono.

Mediante l'uso di Linq2SQL e di Linq2Entities questo compito puo' essere semplificato notevolmente scrivendo un metodo che ci permette di personalizzare la lista estratta nelle sue varie "dimensioni".

Tipicamente abbiamo bisogno di:

  1. Estrarre solo due campi da una tabella che potenzialmente ne contiene molti. Ad esempio solo ID e Nome da una tabella di Province escludendo i dati di estensione, popolazione, etc...
  2. Filtrare gli elementi secondo semplici criteri. Ad esempio solo le Provincie di una determinata Regione
  3. Ordinare gli elementi in modo arbitrario. Ad esempio per Nome visualizzato piuttosto che per Estensione  o numero di abitanti

Grazie all'uso di Lambda Expression con il Framework 3.5 e' possibile esporre le caratteristiche elencate come parametri di un metodo e unendo a questo le potenzialita' dei generics ottenere facilmente qualunque lista in tempo pressoche' nullo. Ecco le due versioni del metodo

   1: // LINQ To SQL
   2:  
   3: static IEnumerable<Item> GetItems<T>(
   4:     Func<T, string> keyFunc,
   5:     Func<T, string> textFunc,
   6:     Func<T, bool> whereSelector,
   7:     Func<T, object> sortFunc)
   8:     where T : class
   9: {
  10:     using (ClassesDataContext dc = new ClassesDataContext())
  11:     {
  12:         return (from entity in dc
  13:                     .GetTable<T>()
  14:                     .Where(whereSelector)
  15:                     .OrderBy(sortFunc)
  16:                 select new Item
  17:                 {
  18:                     Key = keyFunc.Invoke(entity),
  19:                     Value = textFunc.Invoke(entity)
  20:                 }).ToArray();
  21:     }
  22: }
  23:  
  24: // LINQ TO Entities
  25:  
  26: static IEnumerable<Item> GetItems<T>(
  27:     Func<T, string> keyFunc, 
  28:     Func<T, string> textFunc, 
  29:     Func<T, bool> whereSelector, 
  30:     Func<T, object> sortFunc)
  31:     where T : EntityObject
  32: {
  33:     using (Entities entities = new Entities())
  34:     {
  35:         return (from entity in entities
  36:                     .CreateQuery<T>("[" + typeof(T).Name + "]")
  37:                     .Where(whereSelector)
  38:                     .OrderBy(sortFunc)
  39:                 select new Item
  40:                 {
  41:                     Key = keyFunc.Invoke(entity),
  42:                     Value = textFunc.Invoke(entity)
  43:                 }).ToArray();
  44:     }
  45: }

Il metodo, in entrambe le versioni espone 4 parametri che ci suggeriscono l'uso di una Lambda Expression:

  • KeyFunc specifica la chiave
  • textFunc specifica il valore
  • whereSelector ci consente di selezionare gli elementi.
  • sortFunc specifica il campo con cui ordinare gli elementi

L'unica sostanziale differenza tra i metodi per L2S e L2E e nel metodo che usiamo per ottenere accesso alla "tabella" grezza degli elementi. Mentre il DataContext di L2S espone un metodo GetTable<T>() con L2E dobbiamo creare una semplice query che ha come parametro il nome della entity stessa e per questo dobbiamo usare CreateQuery<T>(query);

Ecco come usare il metodo:

   1: static void Main(string[] args)
   2: {
   3:     foreach (Item item in GetItems<Course>(
   4:         a => a.CourseID.ToString(), 
   5:         b => b.Title, 
   6:         c => true, 
   7:         d => d.Title))
   8:     {
   9:         Console.WriteLine(item.Key + " - " + item.Value);
  10:     }
  11:  
  12:     Console.ReadLine();
  13: }

I parametri specificati otterranno una lista di Item - definiti come classi con una proprieta' Key e una Value - dove la chiave sia il valore di CourseID e il testo e' dato da Title. Specificando "true" come terzo parametro otterremo tutti gli elementi della tabella. Infine con il quarto parametro chiediamo di ordinare per Title.

Ho usato questa tecnica in un progetto recente in cui avevo molte form zeppe di DropDownList e devo dire che mi ha fatto risparmiare un bel po' di tempo. Il bello e' che scrivere i parametri e' molto semplice perche' l'intellisense si accorge del tipo che stiamo usando e quindi ci propone i membri della classe tra i quali possiamo comodamente scegliere.


Sto provando Resharper 4.0 da qualche giorno e come al solito lo trovo uno strumento utile e ben fatto che come dalla versione 3.0 è anche finalmente "delicato" nell'uso delle risorse della macchina e di conseguenza maggiormente usabile.

Quello che mi ha un po' indispettito è l'abitudine di Resharper di suggerire l'uso indiscriminato della keyword "var". E' pur vero che questo non ha alcun impatto nell'applicazione, ma la leggibilità del codice ne perde e anche parecchio. Perciò a mio parere la prima cosa da fare non appena installato Resharper è di recarsi nelle opzioni e sotto Code Inspection > Inspection Severity disabilitare le ultime due opzioni della sezione Code redundancies in particolare quelle che recitano:

  • Use 'var' keyword when initializer explicitly declares type
  • Use 'var' keyword when possible

Un bel "Do not show" a queste opzioni ricondurra Resharper alla ragione e ci lascerà scrivere il codice così come si dovrebbe fare cioe esplicitando il tipo ovunque possibile. A mio parere l'uso della keyword 'var' è legittimo esclusivamente quando abbiamo a che fare con degli anonymous types. Ogni altro uso è sostanzialmente uan illegittima scorciatoia.

Technorati Tags: ,

Sto provando Resharper 4.0 da qualche giorno e come al solito lo trovo uno strumento utile e ben fatto che come dalla versione 3.0 è anche finalmente "delicato" nell'uso delle risorse della macchina e di conseguenza maggiormente usabile.

Quello che mi ha un po' indispettito è l'abitudine di Resharper di suggerire l'uso indiscriminato della keyword "var". E' pur vero che questo non ha alcun impatto nell'applicazione, ma la leggibilità del codice ne perde e anche parecchio. Perciò a mio parere la prima cosa da fare non appena installato Resharper è di recarsi nelle opzioni e sotto Code Inspection > Inspection Severity disabilitare le ultime due opzioni della sezione Code redundancies in particolare quelle che recitano:

  • Use 'var' keyword when initializer explicitly declares type
  • Use 'var' keyword when possible

Un bel "Do not show" a queste opzioni ricondurra Resharper alla ragione e ci lascerà scrivere il codice così come si dovrebbe fare cioe esplicitando il tipo ovunque possibile. A mio parere l'uso della keyword 'var' è legittimo esclusivamente quando abbiamo a che fare con degli anonymous types. Ogni altro uso è sostanzialmente uan illegittima scorciatoia.

Technorati Tags: ,

Leggo che qualcuno ha inviato una lettera aperta al team di sviluppo dell'Entity Framework per sollecitare alcune modifiche architetturali prima della release che ormai dovrebbe essere imminente:

http://efvote.wufoo.com/forms/ado-net-entity-framework-vote-of-no-confidence/

Qui la risposta abbastanza estensiva di uno dei program manager del team EF

http://blogs.msdn.com/timmall/archive/2008/06/24/vote-of-no-confidence.aspx

Non esprimo opinioni... prima voglio leggermeli entrambi per bene.

Technorati Tags: ,

Le due settimane che sono iniziare ieri sono dense di appuntamenti per il nostro gruppo e ci aspettano ben due eventi GRATUITI che si terranno il 16 e il 29 Maggio.

Un appuntamento da non perdere per tenersi aggiornati sulla piattaforma Microsoft per lo sviluppo su web. Andrea Dottor infatti oltre a mostrare alcune delle feature di ASP.NET 3.5 mostrerà anche una preview di alcuni dei controlli rilasciti nella recente beta della Service Pack 1. Inizio ore 19:00 - leggi >>

Organizzato in collaborazione con 1NN0va, si tratta di un evento ad ampio spettro dedicato agli sviluppatori web. Oltre ad ASP.NET 3.5 vedremo anche Silverlight 2.0 e Windows Communication Foundation 3.5. Gli argomenti saranno esposti da me (Silverlight 2.0). Andrea Dottor (ASP.NET 3.5) e Davide Bedin (WCF). Inizio ore 18:00 - leggi >>

Vi consiglio di prenotare il vostro posto in prima fila al più presto perchè ormai siamo agli sgoccioli.


Ieri nel tardo pomeriggio è stata annunciata la prima beta pubblica della Service Pack 1 del Framework 3.5. Ci era stata annunciata durante il Global Summit, e puntualmente ne da notizia sul suo weblog, assieme ad altri blogger Microsoft.

La Service Pack in questione in realtà a prima vista da l'impressione di essere una nuova release del Framework stesso tante sono le cose nuove che include:

  • ASP.NET Dynamic Data
  • System.Web.Routing
  • Vari miglioramenti all'intellisense
  • Vari miglioramenti sullo sviluppo client
  • Windows Forms Controls
  • Notevoli miglioramenti per WPF
  • ADO.NET Entity Framework and LINQ to Entities
  • ADO.NET Data Services

Ormai il confine tra Service Pack e nuova release è talmente sottile da risultare invisibile. Tanto valeva dare una versione 3.6 e rilasciare un nuovo Framework!

Beh, pazienza...

Link: http://weblogs.asp.net/scottgu/archive/2008/05/12/visual-studio-2008-and-net-framework-3-5-service-pack-1-beta.aspx


Un collega sta lavorando per realizzare alcune stored procedure usando il CLR per risolvere delle esigenze un po' complesse nell'estrarre le informazioni. Poco fa si è imbattuto in una curiosa situazione. Chiamando il metodo SetChar dell'oggetto SqlDataRecord si ottiene un inspiegabile NotSupportedException. Sulle prime il collega credeva di aver sbagliato qualcosa infatti andando a leggere la pare che tutto debba funzionare a dovere. Ma... con il buon Reflector alla mano si vede immediatamente che in effetti il metodo non è supportato:

   1: public virtual void SetChar(int ordinal, char value)
   2: {
   3:     this.EnsureSubclassOverride();
   4:     throw ADP.NotSupported();
   5: }
   6:  

E' evidente che si tratta di un errore nella documentazione. La soluzione in questo caso è di usare il metodo che è un po' più laborioso ma alla fine fa bene il suo dovere.


E' stato rilasciato un training kit relativo i futuri "enhancements" del Framework 3.5. Si tratta di un pacchetto di hands-on-lab che comprende le seguenti tecnologie:

  • ADO.NET Data Services
  • ADO.NET Entity Framework
  • ASP.NET AJAX History
  • ASP.NET Dynamic Data
  • ASP.NET MVC
  • ASP.NET Silverlight controls
  • Per maggiori dettagli leggete questo post:

    http://lostintangent.com/2008/04/16/net-35-enhancements-training-kit/

    Il download pubblico lo trovate qui:

    http://www.microsoft.com/downloads/details.aspx?FamilyID=355c80e9-fde0-4812-98b5-8a03f5874e96&displaylang=en