di .NET e di altre amenità

Entity Framework: Evitare problemi con il merge di file EDMX

Da un po' di tempo lavoro con e, nonostante tutti i conosciuti problemi, in azienda siamo riusciti ad addomesticarlo (o forse lui ha addomesticatio noi :P) e riusciamo a trarne vantaggio, nei limiti da esso imposti. In attesa della prossima versione che pare risolva buona parte dei problemi (ma al momento lo dico solo per sentito dire e non per esperienza diretta), vorrei darvi un tip su come gestire il merge di file EDMX (il Model di Entity Framework per intenderci) che se preso nel verso sbagliato è prolifico di errori.

L'origine del problema è da individuarsi in una formattazione "allegra" del file XML per cui il tag di chiusura di alcune sezioni viene messo in coda all'ultimo elemento figlio e non sulla riga successiva. Un esempio lo si vede nella parte superiore della seguente figura.

Comparazione Per quanto strano possa sembrare questo fatto mette in crisi il merge (io mi riferisco a Team Foundation Server) e spesso (o soprattutto) la persona che lo sta compiendo. Può accadere che aggiungendo una property ad una entità ci si trovi nel caso della parte inferiore della figura soprastante. Come si vede la proprietà Date diventa l'ultima dell'EntityType. Un merge manuale comporta la copia di entrambe le righe 107 e 108 ma come spesso accade (e direi "umanamente accade") si tende a copiare solo la riga 108 ottenendo quanto segue:

merged Inutile dire che il risultato è un errore di compilazione che (complice l'entity designer di Visual Studio) si traduce nel fallimento della generazione del codice e in qualche centinaio di incomprensibili errori... tutto ciò significa solo una cosa: tempo perso.

Dopo esserci caduto svariate volte ho elaborato una mia "strategia" personale per il merge di file edmx. L'unica cosa "sana" da fare è prevenire questo genere di errori aprendo manualmente da Visual Studio i due file da comparare nell'editor XML e grazie alla preziosa combinazione di tasti CTRL+SHIFT+D riformattare l'XML correttamente. Una operazioni di pochi istanti, che non richiede nemmeno l'apertura della solution, che però fa evitare errori inutili e forieri perdite di tempo. Una volta riformattati i file con questa combinazione infatti il codice sarà lineare e, sembra impossibile ma è così, si otterranno anche meno merge manuali.

Entity Framework: Il materiale del meeting

E' passata una settimana durante la quale ho un po' staccato. Naturalmente ho dimenticato di pubblicare i sorgenti e le slide dei meeting. Viste le incalzanti richieste eccomi finalmente a pubblicare il materiale che vi avevo promesso. Si tratta più o meno di una serie di esempi riguardanti l'Entity Framework che ho messo assieme mentre lo studiavo. Questi esempi poi sono diventati la base per la sessione dello scorso 12 Dicembre.

Approfitto per ringraziare i partecipanti che hanno dimostrato di apprezzare la sessione - nonostante l'argomento fosse un po' pesante - sia per mezzo dei feedback che contattandomi durante questi giorni per pormi qualche domanda.

Ed ecco il link al materiale: http://blog.boschin.it/download/entityframework.rar

Ancora grazie.

Technorati Tags: ,

Entity Framework vs LINQ TO SQL

Eccon un'utile tabella di paragone di alcune delle feature di Linq2SQL e Entity Framework.

Fonte: Entity Framework vs LINQ TO SQL

Entity Framework: Mappare procedure 'fuori ordinanza'

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:

Entity Framework: Generare le classi Entity da codice

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...

Entity Framework: Vote of no confidence... e le risposte

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: ,

IEnumerable Tales: Aggiungere un costruttore alle entity class di Linq 2 SQL

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: ,