asp.net comments edit

Da pochi giorni è stata rilasciata la prima RTM di xVal, come annunciato qui.
Per chi non lo conoscesse xVal è una libreria per la validazione client-side nelle applicazioni ASP.NET MVC; di fatto ASP.NET MVC 1.0 non è dotato della validazione client-side, al contrario del papà ASP.NET che ha a disposizione un’ottima libreria di validatori.

Personalmente ho avuto modo di utilizzare questa libreria in Dexter, e si è rivelata ottima e di facile implementazione; di fatto, per poterla sfruttare è necessario decorare un binder con gli opportuni attributi (è possibile sfruttare quelli messi a disposizione dal .NET Framework o i validator di Castle), registrare il proprio ValidationModelBinder nel global.asax e poco più.
Le novità di quest’ultima versione sono interessantissime e le trovate elencate qui di seguito:

  • Supporto per i validation summaries;
  • Remote validation (tramite AJAX);
  • Localizzazione Danish, French, Dutch, Polish, Swedish, and Italian;
  • Aggiornamento alle nuove release delle librerie jQuery 1.3.2 e jQuery.Validation 1.5.5;
  • Bug fixing;

Maggiori info sono disponibili nel blog di Steve Sanderson (qui), mentre il dowload è disponibile su codeplex (qui).

asp.net comments edit

Da pochi giorni è stato annunciato, tramite il blog di Scott Guthrie (qui), Microsoft AJAX CDN (Content Delivery Network), ossia un sistema di caching delle principali librerie AJAX utilizzate dagli sviluppatori Microsoft e non.
Nello specifico è possibile trovare tutte le librerie Microsoft Ajax e Jquery, come mostrato dall’elenco seguente:

Url delle librerie Microsoft Ajax distribuite tramite CDN:

  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjax.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjax.debug.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxAdoNet.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxAdoNet.debug.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxDataContext.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxDataContext.debug.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxTemplates.js;
  • http://ajax.Microsoft.com/ajax/beta/0909/MicrosoftAjaxTemplates.debug.js;

Url delle librerie JQuery distribuite tramite CDN:

  • http://ajax.Microsoft.com/ajax/jQuery/jquery-1.3.2.js;
  • http://ajax.Microsoft.com/ajax/jQuery/jquery-1.3.2.min.js;
  • http://ajax.Microsoft.com/ajax/jQuery/jquery-1.3.2-vsdoc.js;
  • http://ajax.Microsoft.com/ajax/jQuery/jquery-1.3.2.min-vsdoc.js;

Questo tipo di approccio dovrebbe fornire delle migliori performances sia del client che del server; quest’ultimo si troverrebbe a gestire un minor numero di richieste e, conseguentemente, un minor traffico, mentre il client effettuerebbe il download della libreria da un server geograficamente più vicino a lui e, nel caso l’utente fosse passato per un sito che già utilizzava la libreria javascript tramite CDN, non effettuerebbe il download sfruttando la cache del browser.

È già possibile utilizzare questo sistema di rilascio delle librerie Javascript mettendo il percorso completo per i file JS, come mostrato dallo snippet seguente:




Ovviamente con la prossima versione di Microsoft ASP.NET AJAX sarà incluso il supporto e basterà specificare, tramite un apposito attributo, se si vorrà sfruttare o no questa tipologia di rilascio, come mostrato di seguito:

Un interessante post - che trovate qui - spiega eventuali problemi che si possono riscontrare con tale libreria, nel caso in cui il proprio sito sia un https o il client abbia un alto numero di cookies per il dominio microsoft.com.

Ciauz

various comments edit

Come ogni vero blogengine, anche Dexter ha un’apposita sezione per le statistiche dei propri posts. Anche se il BackOffice di Dexter non brilla per estetica e funzionalità, ho deciso di aggiungere un’interessante feature (la vera motivazione è che non avevo voglia di lavorare ad altro), che fornisce informazioni aggiuntive sul visitatore grazie all’IP di provenienza.

GeoBytes è un sito che permette di attingere a diverse informazioni dato un’indirizzo IP; offre inoltre la possibilità di sfruttare queste informazioni nelle proprie applicazioni (tramite un’apposita webrequest) .
In pratica è sufficiente interrogare l’indirizzo seguente (ovviamente l’indirizzo IP va cambiato) :
http://www.geobytes.com/IpLocator.htm?GetLocation&template=php3.txt&IpAddress=93.34.55.138

per ottenere un’output come quello mostrato di seguito:

<html> 
<head> 
 
<meta name="known" content="true"> 
<meta name="locationcode" content="ITLOMILA"> 
<meta name="fips104" content="IT"> 
<meta name="iso2" content="IT"> 
<meta name="iso3" content="ITA"> 
<meta name="ison" content="380"> 
<meta name="internet" content="IT"> 
<meta name="countryid" content="119"> 
<meta name="country" content="Italy"> 
<meta name="regionid" content="2246"> 
<meta name="region" content="Lombardia"> 
<meta name="regioncode" content="LO"> 
<meta name="adm1code" content="IT09"> 
<meta name="cityid" content="11937"> 
<meta name="city" content="Milano"> 
<meta name="latitude" content="45.4670"> 
<meta name="longitude" content="9.2000"> 
<meta name="timezone" content="+01:00"> 
<meta name="certainty" content="93"> 
 
<title>PHP2 Template</title> 
</head> 
<body></body> 
</html> 



“Parsando” i dati si può ottenere un risultato simile al seguente:

14-Sep-09 21-00-15 14-Sep-09 21-00-35 14-Sep-09 21-00-57

P.S.: il servizio è gratuito fino ad un certo numero (non ho capito bene a quanto equivale) di interrogazioni in un determinato lasso di tempo. Se si decide di utilizzare senza limiti il servizio è necessario abbonarsi.

orm comments edit

NHibernate tende a mappare gli Enums come stringhe nel database; questo a volte può non essere un problema, ma personalmente preferisco memorizzarli in un intero, o ancora meglio un tynint, infatti con questo tipo di formato si ha un netto risparmio di spazio, ma anche un vantaggio per eventuali query.

NHibernate permette di cambiare il modo di persistere un tipo di oggetto del nostro domain model sul database tramite delle apposite classi; tradotto più semplicemente permette di avere una classe sulla propria entity e un semplice campo nel database (ad esempio un intero o un varchar), come potrebbe essere il CultureInfo che, in una classe come quella mostrata di seguito, è un oggetto, mentre sul database una semplice string tipo “en-US”.

public class Comment : EntityBase
{
    public virtual Item Item { get; set; }
    public virtual DateTime CommentDate { get; set; }
    public virtual string Message { get; set; }
    public virtual string Name { get; set; }
    public virtual string Email { get; set; }
    public virtual bool Notify { get; set; }
    public virtual string WebSite { get; set; }
    public virtual bool Approved { get; set; }
    public virtual bool IsSpam { get; set; }
    public virtual System.Globalization.CultureInfo Culture { get; set; }
}

Lo stesso approccio può essere utilizzato per gli Enums, quindi un enum come il seguente:

public enum BlogRollFriendType : short 
{
    Nothing = 0,
    Contact = 1,
    Friend = 2,
    Acquaintance = 3
}

può essere persistito come un tinyint sul database grazie ad uno UserType come quello mostrato di seguito:

public class GenericEnumMapper<T> : IUserType
{
    #region IUserType Members

    public new bool Equals(object x, object y)
    {
        if (ReferenceEquals(x, y)) return true;
        if (x == null || y == null) return false;
        return x.Equals(y);
    }

    public int GetHashCode(object x)
    {
        return x.GetHashCode();
    }

    public object NullSafeGet(IDataReader rs, string[] names, object owner)
    {
        object obj = NHibernateUtil.Int16.NullSafeGet(rs, names[0]);

        if (obj == null)
            return null;

        return (T) obj;
    }

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        if (value == null)
            ((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;
        else
            ((IDataParameter) cmd.Parameters[index]).Value = (short) value;
    }

    public object DeepCopy(object value)
    {
        return value;
    }

    public object Replace(object original, object target, object owner)
    {
        return original;
    }

    public object Assemble(object cached, object owner)
    {
        return cached;
    }

    public object Disassemble(object value)
    {
        return value;
    }

    public SqlType[] SqlTypes
    {
        get { return new[] {new SqlType(DbType.Int16)}; }
    }

    public Type ReturnedType
    {
        get { return typeof (T); }
    }

    public bool IsMutable
    {
        get { return false; }
    }

    #endregion
}

L’implementazione è piuttosto semplice, l’unica accortezza che bisogna avere per poter persistere l’enum come tinyint è specificare lo usertype nel mapping, come mostrato di seguito:

public BlogRoll()
{
    Table("BlogsRoll");
    DynamicUpdate();
    LazyLoad();
    Cache.NonStrictReadWrite();

    Id(x => x.ID)
        .GeneratedBy.Identity();

    Map(x => x.Name)
        .Not.Nullable()
        .Length(100);

    Map(x => x.Link)
        .Not.Nullable()
        .Length(100);

    Map(x => x.IsMyBlog);
    Map(x => x.FriendType)
        .CustomType(typeof(Dexter.NHibernate.Helpers.UserTypes.GenericEnumMapper<BlogRollFriendType>));

    Map(x => x.GeographicalType)
        .CustomType(typeof (Dexter.NHibernate.Helpers.UserTypes.GenericEnumMapper<BlogRollGeographicalType>));
}

Lo screenshot seguente mostra la struttura della tabella:
table.

orm comments edit

Un buon mapping è la base di un buon risultato ottenibile tramite l’uso di un O/RM.
Anche se in molti esempi può sembrare semplice effettuare il mapping, in applicazioni reali (o con particolari richieste) questo può essere tutt’altro che semplice.
In Dexter ho avuto la necessità di realizzare diversi UserType per gestire enumerati, timezone, conversioni, ecc, ed ovviamente, essendoci una logica di calcolo dietro, questi andrebbero testati per evitare di avere un’incongruenza di dati o, nel peggiore dei casi, degli errori.
FluentNHibernate mette a disposizione delle classi che permettono di effettuare lo UnitTest del mapping verificando così che la CRUD della entity funzioni a dovere; quindi, nello specifico, si ha a disposizione un mini Framework per testare le seguenti operazioni:

  • Inserimento della entity;
  • Modifica della entity;
  • Recupero della entity;
  • Cancellazione della entity;

Lo snippet seguente mostra come effettuare questo tipo di test con poche righe di codice, avendo la certezza che il mapping sia corretto:

[TestMethod]
public void BlogRollMap()
{
    new PersistenceSpecification<BlogRoll>(session)
        .CheckProperty(c => c.ID, 1)
        .CheckProperty(c => c.Co_worker, true)
        .CheckProperty(c => c.Colleague, true)
        .CheckProperty(c => c.Crush, true)
        .CheckProperty(c => c.Date, true)
        .CheckProperty(c => c.FamilyType, BlogRollFamilyType.Sibling)
        .CheckProperty(c => c.FriendType, BlogRollFriendType.Contact)
        .CheckProperty(c => c.GeographicalType, BlogRollGeographicalType.Neighbor)
        .CheckProperty(c => c.IsMyBlog, true)
        .CheckProperty(c => c.Link, "http://www.microsoft.com")
        .CheckProperty(c => c.Met, true)
        .CheckProperty(c => c.Muse, true)
        .CheckProperty(c => c.Name, "Microsoft")
        .CheckProperty(c => c.Position, 1)
        .CheckProperty(c => c.Sweetheart, true)
        .CheckProperty(c => c.Username, "imperugo")
        .VerifyTheMappings();
}

Inoltre per condizioni particolari è possibile specificare, tramite l’apposito overload del costruttore, una classe IEqualityComparer per effettuare una comparazione custom dei dati, come mostrato di seguito:

public class SiteConfigurationEqualityComparer : IEqualityComparer
{
    public new bool Equals(object x, object y)
    {
        if (x == null || y == null)
            return false;

        if (x is TimeZoneInfo && y is TimeZoneInfo)
            return ((TimeZoneInfo)x).Id == ((TimeZoneInfo)y).Id;

        if (x is ReCaptcha && y is ReCaptcha)
        {
            var r1 = (ReCaptcha)x;
            var r2 = (ReCaptcha)y;

            return (r1.Enable == r2.Enable &&
                    r1.PrivateKey == r2.PrivateKey &&
                    r1.PublicKey == r2.PublicKey &&
                    r1.Theme == r2.Theme);
        }
}

Maggiori info sono disponibili qui.

Ciauz

.net comments edit

Lo scrivo qui con la speranza di non dimenticarlo, comunque dopo il setup del Service Pack 1 per Visual Studio 2008 (sia Database Edition che Team Suite) va installata la GDR 2 che potete trovare qui. Così facendo evitate di perdere tre ore di tempo per capire il motivo percui, il Wizard di Visual Studio non si connette al Database.

Un super grazie a Lorenzo per la info.

asp.net comments edit

Fin dalla prima versione di ASP.NET esiste la possibilità di avere un tracciato del ciclo di vita della richiesta web, per poter monitorare e comprendere al meglio ciò che accade nella nostra applicazione web.
Personalmente lo ritengo uno strumento indispensabile quando si è nella situazione di dover lavorare su pagine web particolarmente complesse, in cui i fattori partecipanti alla costruzione dell’output possono rendere difficile l’individuazione di un particolare problema: proprio in questi contesti il Trace di ASP.NET può riverlarsi un ottimo alleato.

Ovviamente questo tipo di problematica si può riscontrare anche per tutte le pagine ricche di chiamate AJAX o Javascript e, per fortuna dello sviluppatore, anche in questo caso è disponibile una libreria client-side che permette di effettuare un Trace delle chiamate.
Di fatto, in una semplice pagina ASP.NET in cui sia presente uno ScriptManager è possibile invocare l’apposito metodo “Sys.Debug.trace” ed il contenuto del messaggio verrà mostrato nella finestra di output di VisualStudio.

Lo snippet seguente mostra l’utilizzo del “trace” su semplici funzioni ajax:
 

<script language="javascript" type="text/javascript">
    function testMessage(param) {
        Sys.Debug.trace(Date() + " - Button1 clicked");
    }
</script>

Lo screenshot seguente mostra la finestra di Output di Visual Studio che presenta il messaggio invocato dal trace via javascript:

8-11-2009 2-05-44 PM

Personalmente non ritengo molto comoda la visualizzazione dei Trace invocati tramite javascript nella finestra di output, in quanto vengono mischiati ad altre informazioni generate da Visual Studio stesso o dal compilatore.
Fortunatamente è possibile aggiungere, in alternativa, una textarea all’interno della pagina web, che mostrerà tutti i Trace client-side. Per poter sfruttare questa modalità è necessario aggiungere una textarea all’interno della pagina web in cui si desidera mostrare il Trace, ed il suo ID dovrà essere “TraceConsole”, come mostrato dallo snippet seguente:

<asp:TextBox Columns="100" Rows="5" runat="server" ID="TraceConsole" TextMode="MultiLine"></asp:TextBox>

Lo screenshot seguente mostra il trace nella textarea in azione.

8-11-2009 2-04-41 PM

Ulteriori informazioni riguardarti il Trace di applicazioni web sono disponibili qui.

various comments edit

Dico “part 1” perché il provider non sembra essere ancora molto stabile, cmq sta di fatto che per più di mezza giornata il blog è rimasto offline per problemi di routing tra il server web e la SAN  contente i files.

Con questo down ho approfittato per effettuare il deploy di una nuova versione di Dexter (sempre più vicino alla prima beta) che include un buon BugFixing ed un BackOffice utilizzabile :).

Speriamo non ricapiti :)
Stay Tuned!