Pare proprio che "Cider", che altri non è che il nome in codice per le estensioni WPF per Visual Studio 2005, sia rimasto un passo indietro rispetto a WPF stesso. All'uscita del Framework 3.0 di ieri infatti non è corrisposta l'uscita della versione definitiva di questo importante componente. La CTP di Novembre, che ieri si è accompagnata alla RTM del Framework nei vari link che si sono trovati in giro infatti soffre di qualche problema, il primo di tutti quello che impedisce di dichiarare namespace custom all'interno di file XAML. Semplicemente il farlo è indispensabile, ma fa scoppiare il designer e qualche volta anche l'intellisense. Sinceramente speravo che con l'uscita della release definitiva di Windows Presentation Foundation si riuscisse a dare uno strumento per usarla in modo produttivo, ma con Expression ancora in beta e "Cider" in CTP temo dovremmo ancora attendere molto.


Ora è ufficiale. A dicembre mi cimenterò anche io nella prova di tenere dei webcast. L'argomento che ho scelto è Windows Presentatin Foundation. E' da un po' che mi cimento in questo ambito e devo dire che ne sono affascinato. Io sono un programmatore web, di nascita, e nonostante abbia lavorato per quasi due anni esclusivamente in Windows Forms, ho sempre sentito la mancanza della mia piattaforma elettiva alla quale sono tornato non appena ho potuto. Con WPF però le cose sono diverse. Fare applicazioni in XAML assomiglia per qualche verso a realizzare pagine web, anzi in definitiva è un po' come prendere il meglio di entrambi i mondi. Immaginate di sviluppare applicazioni web stateful, e avrete più o meno la sensazione che si prova nello scrivere con XAML.

I webcast sono in preparazione e riguarderanno in particolare l'implementazione WPF del DataBinding e la realizzazione di interfacce mediante l'uso di effetti e animazioni. L'idea è quella di realizzare una piccola applicazione, e poi descriverne il funzionamento passo-passo usandola come filo conduttore per entrambi i webcast. Naturalmente se avete richieste non avete che da scrivermi e vedrò di accontentarvi.

Un ringraziamento a che ha messo a disposizione la linea internet, mettedomi così in grado di tenere i webcast che altrimenti non avrei potuto fare dato che la zona in cui abito non è coperta.

Ecco il link alla pagina di iscrizione:

Martedì 14 Dicembre 2006 - ore 14:30

Martedì 21 Dicembre 2006 - ore 14:30

 


Dal Tech-ed stamane è arrivata la notizia che il framework .NET 3.0 è stato rilasciato in versione RTM. Ora finalmente sono disponibile gli strumenti di cui da tempo si parla cioè Windows Presentation Foundation, Windows Communication Foundation e Workflow Foundation. Molti dicono che domani tocchi a Windows Vista. Stiamo a vedere.

Links:


Se per caso vi serve avere un mini web-server, al solo scopo di fare qualche test, c'è un modo semplice di lanciare da codice l'ASP.NET Development Server. Per poterlo fare da un progetto di Visual Studio è sufficiente cercare, via command prompt, il seguente assembly:

c:\WINDOWS\assembly\GAC_32\WebDev.WebHost\8.0.0.0__b03f5f7f11d50a3a\WebDev.WebHost.dll

Dopo averlo referenziato da Visual Studio (è obbligatorio avere Visual Studio perchè questo è un componente che si installa con esso), il seguente codice avvierà un WebServer in local sulla porta 3010:

class Program
{
    
static void Main(string[] args)
    {
        Server server = 
new Server(88, "/imho", "d:\\");
        server.Start();

        Console.ReadLine();
    }
}

Attenzione però che se sviluppate un software usando questa tecnica non potrete mai redistribuirlo per il semplice fatto che l'ASP.NET Development Server è parte di Visual Studio 2005 e la licenza lo impedisce.

Se per caso vi serve un web server opensource, miigliore del vecchio Cassini, alora provate questo che supporta anche completamente ASP.NET.

Link:


Ora che è pubblico il mio ultimo sforzo produttivo, posso fare due cose: la prima è il tornare a bloggare, e la seconda dire due parole con una certa cognizione di causa a proposito di NHibernate e più estensivamente a proposito degli ORM. L'occasione mi è data dall'aver lavorato sul sito di XE.NET utilizzando NHibernate, sul quale è basato cuyahoga che a sua vota è appunto base per il sito.

Inutile dire che NHibernate è magico. Mappi una classe e ti dimentichi (quasi) che questa in realtà è persistente su un database, ti dimentichi persino di che database si tratta e questo, a mio parere, è sia il suo maggior pregio che il suo peggior difetto.

Il fatto è che a me fa anche piacere non dover scrivere SQL (che per inciso adoro), però poi ho il timore che il dimenticarmi che sto lavorando con un database mi porti ad abusarne. Mi spiego meglio: quando scrivo una stored procedure che elabora qualcosa e restituisce dei risultati so sempre esattamente quante volte questa verrà chiamata e addirittura posso decidere di scrivere delle procedure studiate appositamente per evitare troppi roundtrip. Con nhibernate questa sensazione si perde. Sai che stai prelevando o scrivendo dati su un database, ma non sai mai esattamente quanto lo stai impegnando e questo a mio parere è un grave problema che deve essere considerato attentamente. Lo so, Nhibernate ha i meccanismi di cache per evitare troppe chiamate al database, ma sapete bene quanto me che la cache per quanto ben fatta, ha comunque un limite e non può essere considerata panacea di tutti i mali.

Detto questo comunque la mia sensazione è del tutto positiva. Credo di dover trovare il mio modo di usare NHibernate, inventandomi qualche cosa che mi restituisca almeno in parte la padronanza di un mezzo di cui non vorrei perdere il controllo. Però è assodato che prossimamente i miei progetti (ne ho uno in cantiere da un po'... ) godranno di questo strumento.

Il mio consiglio quindi è di provarlo perchè se tra i requisiti di una applicazione c'è (e quando non è così?) anche un tempo di sviluppo ridotto, allora NHibernate è il vostro migliore amico.

powered by IMHO 1.3


Ho letto il post di Lanny stamattina e ho deciso che probabilmente era il caso di postare un esempietto su come sia preferibile lavorare con la reflection per evitare i problemi più comuni. L'esempio di Lanny funziona ed è utile per capire come funziona la reflection, ma dovendo lavorare con essa è opportuno fare affidamento su una serie di classi belle e pronte che il framework mette a disposizione. Oltre a snellire il lavoro eliminando la necessità di riscrivere del codice che è già perfettamente funzionane, consentono soprattutto di avere un modo affidabile di operare conversioni di tipo senza incorrere nei comuni errori dovuti alla globalization. Ecco uno spezzone di codice commentato che mostra come creare un tipo, settarne le proprietà e operare delle conversioni da stringa a data utilizzando le comodissime TypeDescriptor e TypeConverter:

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Globalization;

namespace TestTipi
{
    
class Program
    {
        
static void Main(string[] args)
        {
            
// ottengo il riferimento al tipo da istanziare
            
Type personType = 
                Type.GetType("TestTipi.Person, TestTipi");
            
            
// creo una istanza del tipo
            
object p = 
                TypeDescriptor.CreateInstance(
null, personType, nullnull);
            
            
// ottengo una collection delle property dell'istanzaa
            
PropertyDescriptorCollection personProperties = 
                TypeDescriptor.GetProperties(p);
            
            
// setto FirstName
            
PropertyDescriptor firstName = 
                personProperties.Find("FirstName", 
false);
            firstName.SetValue(p, "Andrea");

            
// setto LastName
            
PropertyDescriptor lastName = 
                personProperties.Find("LastName", 
false);
            lastName.SetValue(p, "Boschin");

            
// setto Birthday
            
PropertyDescriptor birthDay = 
                personProperties.Find("Birthday", 
false);
            TypeConverter converterToDate = 
                TypeDescriptor.GetConverter(birthDay.PropertyType);
            birthDay.SetValue(
                p,
                converterToDate.ConvertFromString(
                    
null
                    CultureInfo.CreateSpecificCulture("en-US"), 
                    "10/27/1968"));

            
// dump sulla console
            
Console.WriteLine(p);
            Console.ReadLine();
        }
    }

    
/// <summary>
    /// 
classina di test
    
/// </summary>
    
class Person
    {
        
private string firstName;
        
private string lastName;
        
private DateTime birthday;

        
public string FirstName
        {
            
get return firstName; }
            
set { firstName = value; }
        }

        
public string LastName
        {
            
get return lastName; }
            
set { lastName = value; }
        }

        
public DateTime Birthday
        {
            
get return birthday; }
            
set { birthday = value; }
        }

        
public override string ToString()
        {
            
return string.Format(
                "{0} {1} ({2})", 
                
this.FirstName, 
                
this.LastName, 
                DateTime.Now.Year - 
this.Birthday.Year);
        }
    }
}

Due note rapide sull'esempio: per creare l'istanza di un tipo è opportuno evitare l'uso di Assemply.LoadFrom() ovunque possibile. piuttosto è preferibile usare come nell'esempio la Type.GetType() passando il nome completo del tipo includendo anche version, culture e publickeytoken qualora l'assembly si trovi in GAC o sia firmato. Usare il riferimento al nome del file non è una buona politica perchè così facendo si esclude tutto il meccanismo di ricerca di fusion. La Type.GetType() è in grado di lavorare perfettamente anche con gli assembly autogenerati a patto che vengano messi in una directory che si trova nell'ambito del probing di fusion.

Secondo: convertire da stringa a qualsiasi tipo e viceversa è un'attività insidiosa tanto che nel framework ci sono svariati errori proprio su questo argomento. Meglio perciò usare la TypeConverter che per inciso è un grado di fare conversioni anche su tipi particolari come ad esempio le Unit. TypeDescriptor ha un bellissimo metodo GetConverter() che è in grado di istanziare l'opportuno convertitore semplicemente passandogli il tipo di destinazione. Okkio quindi che la Culture va passata SEMPRE!

Infine, attenzione ai null. Io nel post non ho messo alcun controllo per non risultare chilometrico, ma e' evidente che ogni volta che chiediamo di istanziare un tipo, un assembly, un convertitore, etc... dobbiamo verificare opportunamente che tutto sia andato a buon fine.

powered by IMHO 1.3


Ci ho messo un pò di più di quello che avevo promesso, ma alla fine eccomi a postare il codice del plugin Lorem Ipsum per Live Messenger 8.0. Il motivo del ritardo è presto detto: mi sono soffermato a cpire come realizzare una feature che mi interessava parecchio, almeno quanto so alletterà i maniaci del messaggino-sul-messenger. ora è possibile immettere i propri messaggi e farli ruotare al posto di quelli generati da Lorem Ipsum.

Giusto per smentire quello che avevo detto qualche post fa, ho scoperto che valorizzando la proprietà SavedState dell'istanza di messenger è possibile salvare delle impostazioni che possano essere riutilizzate dal plugin. Alla fine la soluzione è stata quella di utilizzare il Serializzatore XML dato che le regole di Code Access Security impediscono l'uso finanche di quello binario.

Chi lo voglia scaricare non ha che da cliccare il link riportato qui sotto ed in seguito copiare le due dll che troverà nella cartella bin in un luogo sicuro e poi aggiungerlo per mezzo del pannello diu impostazioni.

Link: http://www.boschin.it/imho/Elite.MSN.LoremIpsum.zip

powered by IMHO 1.3


Stanotte, preso dalla voglia di fare qualcosa di "pazzesco" (o forse dalla voglia di far nulla...) ho rispolverato un post di Giorgio Sardo a proposito dei plugin per Live Messenger. In breve, come mi capita spesso ho buttato giù un po' di codice unendo cose che apparentemente non hanno nulla a che vedere le une con le altre e ne è uscito un Lorem Ipsum Add-in per Live Messenger.

A che serve? Beh, visto che c'è la moda di postare ogni genere di cosa nel messaggio personale di Live Messenger e che io in questo difetto di attenzione e non lo cambio così spesso, ho fatto un generatore causuale di messaggi in latino che lo cambia ogni 60 secondi... provate a starmi dietro ora...

Link: LoremIpsumMsn.zip (37 KB)

Per installarlo dovete dapprima attivare gli add-in di live messenger 8.0 ed in seguito caricare l'assembly LoremIpsumPlugin.dll dal pannello delle opzioni. Apparirà una voce in coda agli stati che serve per attivare il plugin.

Il sorgente a stasera...

powered by IMHO 1.3


Forse non sono in molti che gestiscono la validazione dei parametri di input di un metodo, tuttavia considerato che sarebbe una buona cosa farlo, vi propongo uno snippet che mi sono creato che dovrebbe semplificare questa ripetitiva attività. Lo snippet, che nell'esempio è dedicato al caso delle stringhe, compila per noi una sezione di validazione e ci consente di immettere il nome del parametro e il messaggio che vogliamo dare nell'eccezione.

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  
xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet 
Format="1.0.0">
        <Header>
            <
Title>param_str</Title>
            <Shortcut>
param_str</Shortcut>
            <Description>
Code snippet for string parameter validation</Description>
            <Author>
Andrea Boschin</Author>
            <SnippetTypes>
                <SnippetType>
Expansion</SnippetType>
            <
/SnippetTypes>
        <
/Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <
ID>argument</ID>
                    <ToolTip>
Argument name</ToolTip>
                    <Default>
name</Default>
                <
/Literal>
                <Literal>
                    <
ID>messageNull</ID>
                    <ToolTip>
Exception Message for null string</ToolTip>
                    <Default>
Argument cannot be null</Default>
                <
/Literal>
                <Literal>
                    <
ID>messageEmpty</ID>
                    <ToolTip>
Exception Message for empty string</ToolTip>
                    <Default>
Argument cannot be empty</Default>
                <
/Literal>
            <
/Declarations>
            <
Code Language="csharp">
                <![CDATA[if 
($argument$ == null)
                throw new ArgumentNullException("$argument$", "$messageNull$");
                if 
($argument$ == string.Empty)
                throw new ArgumentException("$argument$", "$messageEmpty$");$end$]]>
            <
/Code>
        <
/Snippet>
    <
/CodeSnippet>
<
/CodeSnippets>

Alla vostra fantasia tutte le possibili varianti.

powered by IMHO 1.3

 


Un esperienza pratica di lavoro con WebServices, applicazioni legacy, e Framework .NET mi ha suggerito l'argomento dell'articolo che questa settimana è apparso sulle "colonne" di UgiDotNet. L'articolo dimostra come, sfruttando gli strumenti del framework 2.0 - mi riferisco a reflection e generics - si possa abbattere notevolmente il tempo di sviluppo di una applicazione. Non solo, l'implementazione è anche un bell'esempio di design, che grazie ad alcuni accorgimenti ha reso flessibile ed adattabile a future esigenze il codice. 

Tenete presente che questo lavoro ci ha consentito di evitare la realizzazione di una cinquantina di convertitori ad-hoc che avrebbero richiesto un tempo esponenzialmente più elevato. Un ringraziamento al mio collega Roberto che è stato il primo a sentire l'esigenza di rendere parametrico in qualche modo il lavoro che stava facendo, stimolandomi nel trovare la soluzione che ora potete leggere.

Link: http://www.ugidotnet.org/articles/articles_read.aspx?ID=109

powered by IMHO 1.3


Prosegue con questo post la saga, iniziata quando ho parlato degli ExpressionBuilders, dedicata alle risorse tipicamente utilizzate nelle applicazioni ASP.NET per depositare stringhe, e in genere proprietà da utilizzare nel rendering dell'interfaccia. In quel post spiegavo come realizzare un ExpressionBuilder che permettesse facilmente dei leggere le risorse da un database anziche da un più consueto file resx. Esiste tuttavia un altro metodo, per fare la medesima cosa, che in realtà è quello che il framework indica come quello corretto. Chi avesse provato a realizzare l'ExpressionBuilder forse si sarà reso conto che pur funzionando egregiamente esso ha delle limitazioni fastidiose. Giusto per indicarne una per tutte, quella che a me ha fatto approfondire ulteriormente l'argomento, qualora vi trovaste a localizzare un SiteMap ben presto vi renderete conto che in questo l'ExpressionBuilder non vi è di alcun aiuto. Il motivo è presto detto: la funzione degli ExpressionBuilder non è quella di leggere risorse, ma semplicemente quello che tipicamente viene usato per questa operazione, il ResourceExpressionBuilder è solo una minima sfaccettatura all'interno dell'architettura del framework dedicata all'argomento.

Se come ho fatto io imbracciate il vostro reflector, e lo puntate sulla classe System.Web.SiteMapNode, vi accorgerete che la proprietà Title (una di quelle che è passibile di localizzazione), racchiude al suo interno molta più logica di quella che ragionevolmente ci si potrebbe aspettare. Ecco un estratto da Reflector:

public virtual string Title
{
  
get
  
{
    
if (this._provider.EnableLocalization)
    {
      
string text1 = this.GetImplicitResourceString("title");
                 
      
if (text1 != null)
        
return text1;

      text1 = 
this.GetExplicitResourceString("title", this._title, true);

      
if (text1 != null)
        
return text1;
    }

    
if (this._title != null)
        
return this._title;

    
return string.Empty;
  }
}

Procedendo ulteriormente nell'esplorazione in breve si scoprira che il codice porta ad istanziare il medesimo ResourceExpressionBuilder e quindi in definitiva tentare di localizzare una proprietà della SiteMap corrisponde ad usare l'ExpressionBuilder "Resources". Di primo acchito questo mi è parso davvero strano. L'idea che mi ero fatto era che a quel punto si potessero usare esclusivamente i consueti file resx per localizzare una SiteMap.

In realtà nel framework esiste una interfaccia IResourceProvider, che è il punto giusto cui agganciarsi per spostare la fonte delle risorse dai normali file al database. Un po' celato all'interno del DOM del web.config si trova un attributo dell'elemento <globalization> denominato resourceProviderFactoryType. In tale attributo si dovrà immettere il tipo, derivato da ResourceProviderFactory, che ha il compito di creare il ResourceProvider deputato a recuperare le risorse. Mi rendo conto che detto così pare un po' complesso, ma questo meccanismo è molto efficace perchè consente di avere diversi ResourceProvider che lavorano assieme, istanziati alla bisogna in base al tipo di risorse che si desidera prelevare. In particolare il ResourceProviderFactory dispone di due metodi, uno per le risorse cosidette "locali" e uno per quelle "globali". Per comprendere la distinzione si dovra pensare alle risorse locali come quelle dedicate ad una singola pagina, infatti il relativo metodo richiede in ingresso il path della pagina, mentre per globali si intendono quello usate da tutte le pagine dell'applicazione. In quest'ultimo caso il metodo richiede una "classKey" cioè un qualificatore che permette di distinguere dei gruppi di risorse.

Non è finita però. Dopo aver implementato il ResourceProviderFactory dovremmo procedere all'implementazione di IResourceProvider che richiede la realizzazione di due metodi. Il primo per la lettura di una singola risorsa, e il secondo che restituisce un IResourceReader, che serve al runtime per enumerare le risorse presenti. La classe che implementa IResourceReader sarà lanostra terza ed ultima fatica. Ecco di seguito il codice di entrambe le classi:

public class SqlResourceProvider : IResourceProvider
{
    
public object GetObject(string key, CultureInfo culture)
    {
        
// leggi una singola risorsa 
        return
GetResourceFromDB(key, culture);
    }

    
public System.Resources.IResourceReader ResourceReader
    {
        
get
        
{
            
// leggi un set di risorse
            
return new SqlResourceReader(
                GetResourcesFromDB(CultureInfo.CurrentUICulture));
        }
    }
}
        
public class SqlResourceReader : IResourceReader
{
    
private Dictionary<stringobject> resources;

    
public SqlResourceReader(Dictionary<stringobject> resources)
    {
        
this.resources = resources;
    }

    
public IDictionaryEnumerator GetEnumerator()
    {
        
return resources.GetEnumerator();
    }

    
public void Close()
    { }
        
    
public IEnumerator IEnumerable.GetEnumerator()
    {
        
return resources.GetEnumerator();
    }

    
public void Dispose()
    {
        Close();
    }
}

L'istanza del ResourceProvider andrà creata nel ResourceProviderFactory che registreremo in configurazione. In questo modo avremo interposto il nostro provider a tutti i tentativi di lettura di risorse di localizzazione effettuati da parte dei componenti del framework, ed in questo modo potremmo facilmente localizzarli. Anche la SiteMap di cui ho detto poco fa.
 

powered by IMHO 1.3