giovedì 23 dicembre 2010

E' disponibile Mutuo Mobile!

Eccomi qui dopo una lunga lunghissima assenza.
Per sopperire all'assenza di attività tecnica (ormai è da quasi un anno che ho cambiato ruolo nel mio lavoro e non ho più toccato una riga di codice ahimè...) mi sono voluto addentrare in un mondo che mi sta affascinando sempre più...Android!

Da Maggio sono in possesso di un nuovo (beh nuovo, ormai è da ritenersi vecchio, mannaggia!) smartphone Android, l'HTC Desire.
Visto l'impennata di popolarità avuta da questo sistema operativo e dal conseguente incredibile aumento del numero di applicazioni presenti, mi sono detto: perchè non provo a fare un'applicazione anch'io?
Dopo tutto coglierei tra l'altro l'occasione di cimentarmi in un mondo diverso, quello dell'open source, di Java per intendersi, dopo anni di dedizione professionale al mondo Microsoft :)

Ed ecco arrivato il giorno in cui sono riuscito a pubblicare la mia prima (piccola!) applicazione: Mutuo Mobile.
Quello dei mutui è un mondo che ultimamente mi sono trovato ad affrontare in modo approfondito, dunque ne ho preso spunto e ne ho fatto un'applicazione.
Questa prima versione permette di calcolare una delle quattro informazioni tipiche di un mutuo, conoscendo le altre tre: l'importo, la durata, il tasso e la rata.
Non potevo esimermi dall'inserire il calcolo del piano di ammortamento con la possibilità di esportarlo in CSV e di inviarselo per posta.

Nelle prossime settimane ho in mente di aggiornarlo con una nuova funzionalità, che permetterà di simulare l'importo delle rate e degli interessi del mutuo utilizzando l'andamento reale degli indici degli ultimi 10 anni.
Anche questa funzionalità è nata da una mia esigenza personale di cercare di districarmi tra le mille varianti di mutuo che mi sono stati offerti: fisso, variabile, misto, variabile con il tetto, e chi più ne ha ne metta :)

Che ne dite? :)

Non mi resta che darvi il link al market dove potete provarla e darmi un feedback :


Android app on Google Play



A presto!

venerdì 12 marzo 2010

Addin di Outlook per l'integrazione con TFS

Vi voglio segnalare un ottimo plugin per Outlook che offre alcune utili funzionalità di integrazione con Team Foundation.

Con questo addin è possibile creare "al volo" a partire da una mail, un task, un bug o in genere un qualsiasi work item configurato nel vostro progetto. Il plugin provvede inoltre a inserire il testo della mail direttamente nel field che avete impostato a livello di configurazione, così come l'oggetto della mail.
Ciò può tornarvi molto utile ,ad esempio, nella creazione di work item di tipo Bug, la quale è spesso conseguenza di una segnalazione via mail. Con questo addin, quindi, potrete a partire dalla mail inserire il Bug in TFS e monitorare in futuro il suo stato.

Infine due utilissimi bottoni hanno la funzione, rispettivamente, di aprire direttamente un work item creato in precedenza a partire dalla mail selezionata e di avere un rapido accesso alle query configurate sul server.

Ecco i link per scaricare l'addin:

Addin per Outlook 2003

Addin per outlook 2007

giovedì 11 marzo 2010

Modificare un Work Item Type in Team Foundation

Lavorando con Team Foundation diventa fondamentale adattare il modello di gestione del progetto che ci fornisce Microsoft out of the box (MSF for AGILE Software Development o il MSF for CMMI Improvement) al nostro modello che più preferiamo o più aderisce alla nostra realtà lavorativa.
A questo proposito vi descrivo come è possibile cambiare o aggiungere un Work Item Type, che è di fatto uno dei componenti principali di tutto il modello di gestione progettuale (che in TFS viene chiamato Process Template).
Innanzitutto è necessario che vi installiate i Power Tools di Visual Studio che vi aggiungono delle utili funzionalità per la gestione dei process template di TFS:
Power Tools for Visual Studio 2008
Nel caso volessimo modificare un Work Item Type già esistente è necessario aprire il menu di Visual Studio Tools/Process Editor/Work Items Type/Open WIT from Server, scegliere un progetto di TFS e un work item type.
A questo punto il work item type (WIT) viene aperto in editing. Nel primo tab Fields è possibile aggiungere un nuovo metadato al WIT o modificarne uno esistente. Prendiamo come esempio la modifica del WIT Task del nostro progetto, con l'aggiunta di un nuovo campo che rappresenti la difficoltà del task. Nel tab Fields premere New e inserire i dati del nuovo campo:



Nel tab Rules, invece, è possibile definire tutte le logiche di validazione, i valori di default, ecc... del campo in questione. Nel nostro caso inseriamo i possibili valori che il campo può assumere, utilizzando la rule ALLOWEDVALUES:



Ora è necessario inserire il nuovo campo nel form che TFS mostra nel momento in cui viene creato un nuovo Work Item. Per fare ciò, aprire il Tab Layout e inserire un nuovo controllo nella gerarchia dei controlli del form. Facciamo l'esempio di voler inserire il campo Difficoltà sotto il titolo del task:



Prememdo su Preview Form è possibile avere l'anteprima del form modificato:



Salvando il file WIT le modifiche sono già pubblicate sul server di TFS. E' possibile pero' lavorare in modalità "offline" cioè esportare un Work Item Type da TFS in un file con estensione WIT (menù Tools/Process Editor/Work Items Type/Export WIT), eseguire le modifiche e importare il file nuovamente in TFS (menù Tools/Process Editor/Work Items Type/Import WIT).

mercoledì 3 marzo 2010

Mantenere lo stato dei controlli in ASP.NET MVC

Una delle prime cose che si imparano quando si affronta per la prima volta il mondo di ASP.NET MVC provenendo da un'esperienza di WebForms, è che la grande "invenzione" che ha reso tanto popolare quest'ultima tecnologia non esiste più, ossia il ViewState.

Dopo un momento di smarrimento, si cerca di capire come farne a meno (e capirete che di vantaggi ce ne sono!)

Ovviamente i creatori di MVC non ci hanno lasciato con le mani vuote, ma hanno pensato a un modo articolato ma efficace per ottenere lo stesso risultato. In realtà quello che si può ottenere "gratis" dal framework è il mantenimento del valore effettivo di un input control (dunque non le altre proprietà come Visible, Color, ecc..)

Prendiamo ad esempio una textbox:

<%= Html.TextBox("SearchText","testo di default")%>

e il seguente Action method invocato dal submit presente nella pagina della textbox:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Search(string SearchText)
{
    return View();
}

Mandando in esecuzione il codice vedremo che alla prima apertura della pagina la textbox avrà il valore "testo di default", modificando la textbox e premendo il bottone di submit la textbox viene renderizzata con il valore modificato. La textbox ha mantenuto lo stato!

Cosa è successo??

Tutto ciò è dovuto al fatto che "per convenzione" il framework MVC valorizza i controlli presenti in una pagina prendendone i valori secondo una scala di priorità di seguito riportata:

1 MVC controlla se è presente un valore nella proprietà ModelState["SearchText"].Value.AttemptedValue.   ModelState è una collection in cui sono aggiunti TUTTI i valori che i model binder trasferiscono al controller. La collection diventa utile quando si vuole far apparire un messaggio di errore di validazione e non si vuole perdere al post della pagina quello che l'utente ha inserito.

2 MVC controlla se è presente un valore di default del controllo (nel nostro caso la stringa "testo di default")

3 MVC controlla se è presente un valore per l'elemento ViewData["SearchText"]. ViewData è la collection che il Controller passa alla View con tutti i dati sul modello.

4 MVC controlla se è presente un valore per l'elemento ViewData.Model.SearchText . ViewData.Model è l'oggetto che viene passato dal Controller alla View come modello

Tornando al nostro caso, al primo accesso della pagina scatta la regola 2. Il ModelState, infatti, è vuoto mentre la stringa di default è valorizzata.
Al post della pagina, il "miracolo" è possibile proprio per la presenza della proprietà ModelState["SearchText"].Value.AttemptedValue (regola 1). Notare che questa è stata valorizzata poichè la proprietà SearchText è un parametro di ingresso dell'action method e dunque è stata inserita nel ModelState dal model binder. Qualora non ci fosse tale parametro, il ModelState["SearchText"] non sarebbe valorizzato e noi al post della pagina troveremmo di nuovo la stringa "testo di default" (di nuovo regola 2).

E' bene dunque tenere sempre a mente queste priorità in modo da non incorrere in comportamenti non previsti.

giovedì 18 febbraio 2010

Filtrare i dati in SSAS con la funzione UserName()

 Mi è capitato di dover fare in modo che gli utenti che accedessero ad un Cubo di Analisys Services 2005 vedessero solo i dati di loro competenza.

In pratica nel mio caso avevo una dimensione di tipo gerarchico che mi definiva la gerarchia aziendale del personale (tipica tabella degli utenti con l'id del superiore) e il requisito era quello che ogni utente avrebbe dovuto vedere  solo i dati che si riferivano a lui stesso o ai suoi sottoposti.

Imbattendomi nella configurazione dei permessi di SSAS ho trovato la seguente soluzione che utilizza la funzione UserName() che restituisce l'utente corrente:

- Innanzitutto è necessario creare un Ruolo ad hoc all'interno del cubo a cui assegnare tutti gli utenti che devono essere soggetti a tale comportamento.

- Se si vuole fare in modo che l'utente veda esclusivamente solo la gerarchia che lo riguarda (gli altri utenti non saranno nemmeno visibili), è necessario impostare nella configurazione del ruolo all'interno del tab Dimension Data nella sezione Allowed member set la seguente espressione MDX :
      
             DESCENDANTS( [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]") )

- Se si vuole invece limitare esclusivamente la visualizzazione delle misure per la gerarchia che lo riguarda senza limitare la visualizzazione dell'intera gerarchia, è necessario impostare nella configurazione del ruolo all'interno del tab CellData nella sezione Allow reading of cube content la seguente espressione MDX:

             ISANCESTOR( [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]"),[UtentiDIM].[Gerarchia].CURRENTMEMBER )
             OR  [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]") IS [UtentiDIM].[Gerarchia].CURRENTMEMBER

Ovviamente tali espressioni valgono nel caso più semplice che il nome del membro corrisponda esattamente agli username degli utenti. In caso contrario è necessario modificare leggermente l'espressione MDX.

martedì 16 febbraio 2010

Unit test del Data Layer con TransactionScope

Nel momento in cui si deve eseguire uno Unit Test per testare il funzionamento delle classi per l'accesso ai dati, si possono percorrere due strade:

- la prima è quella di utilizzare un database di test che risiede completamente in memoria, come ad esempio SqlLite, un ottimo motore SQL che possiede appunto la caratteristica di poter girare senza installazioni e senza file fisici. Ovviamente il vantaggio di questa soluzione è che ad ogni avvio dei nostri test, abbiamo sempre a disposizione un database pulito. Per inciso, ricordo che Nhibernate possiede una comoda funzionalità di generazione degli script per la creazione dello schema del database a partire dal file di mapping.

- la seconda è quella invece di utilizzare un vero e proprio database di test.

Nonostante sia preferibile la prima soluzione, in alcuni casi questa è una strada difficilmente percorribile, ad esempio quando si utlizzano tipi di dati che non sono supportati in SqlLite.

Utilizzando un database di test, è necessario però garantire che tutte le operazioni che vengono eseguite sul database durante i test, debbano essere ripristinate al loro termine.
A tal fine torna molto utile l'utilizzo della classe TransactionScope (assembly System.Transaction), che ci garantisce che tutti comandi lanciati sul database siano contenuti all'interno di una transazione del quale verrà invocato il RollBack al termine dei test.

Nell'esempio utilizzo NUnit come framework per lo unit test, ma il concetto è valido ovviamente per tutti i framework.

        protected TransactionScope _transactionScope;

        [SetUp]
        public void Setup()
        {
            _transactionScope = new TransactionScope();
        }

        [TearDown]
        public void TearDown()
        {
            _transactionScope.Dispose();
        }

Se questo codice lo inseriamo in una classe base che tutte le classi di test devono estendere, ecco che abbiamo un sistema sicuro per eseguire i nostri test delle classi del Data Layer

giovedì 11 febbraio 2010

Nhibernate custom type mapping e dati geospaziali

Ormai è da un pò di tempo che utilizzo Nhibernate nei progetti che seguo e ogni tanto capita di dover affrontare nuove esigenze.
Riporto qui il caso in cui si debba avere a che fare con il mapping di un classe che possiede proprietà di un tipo che non rientra tra quelli gestiti direttamente da Nhibernate.

A titolo di esempio riporto il caso in cui una classe abbia una proprietà di tipo SqlGeography. La classe SqlGeography è contenuta nell'assembly Microsoft.SqlServer.Types ed è la classe che rappresenta il data type Geography di SQL Server 2008 non direttamente supportato da NHibernate.
Il data type Geography è la rappresentazione binaria delle coordinate geografiche di un punto sulla terra (coppia latitudine longitudine).
Quello che vogliamo ottenere è il mapping della proprietà di tipo SqlGeography e il campo sul database di tipo Geography (rappresentazione binaria del testo "POINT (latitudine longitudine)")

Per ottenere ciò è necessario innanzitutto creare una nuova classe che abbia lo scopo di definire il modo in cui NH debba eseguire il mapping sul database. Tale classe deve ereditare dall'interfaccia IUserType (namespace NHibernate.UserTypes) e implementare i seguenti metodi:

public class SqlGeographyUserType : IUserType
    {
        public object DeepCopy(object value)
        {
            if (value == null)
                return null;
            var sourceTarget = (SqlGeography)value;
            SqlGeography targetGeography = SqlGeography.Point(sourceTarget.Lat.Value, sourceTarget.Long.Value,
                                                              sourceTarget.STSrid.Value);
            return targetGeography;
        }
       
        public object Assemble(object cached, object owner)
        {
            return DeepCopy(cached);
        }
       public object Disassemble(object value)
        {
            return DeepCopy(value);
        }

Assemble e Disassemble vengono chiamati da NH rispettivamente durante la lettura e la scrittura nella cache di secondo livello, dunque si devono preoccupare di restituire o creare l'oggetto nella sua versione in cache. In questo caso l'oggetto in cache sarà identico a quello restituito utilizzando il metodo DeepCopy.

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

Il metodo Equals è utilizzato da NH per verificare se la proprietà è diversa da quella memorizzata nella snapshot salvata in memoria, così da provvedere al salvataggio su database.

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

        public bool IsMutable
        {
            get { return true; }
        }

IsMutable deve restituire true se la classe è Mutable. Per le classi non Mutable infatti (cioè quelle per cui NH non debba fare l'insert e l'update) NH ha alcune ottimizzazioni delle performance.

        public object NullSafeGet(IDataReader rs, string[] names, object owner)
        {
            object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);
            if (prop1 == null)
                return null;
            SqlGeography geo = SqlGeography.Parse(new SqlString(prop1.ToString()));

            return geo;
        }

NullSafeGet viene chiamato da NH per ottenere l'istanza della proprietà mappata (nel nostro caso l'oggetto di tipo SqlGeograhy) a partire dal datareader utilizzato per leggere dal database. Dalla lettura della stringa del tipo "POINT (lat lon)" otteniamo un istanza del tipo SqlGeography.

        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 = ((SqlGeography) value).STAsText().Value;
            ;
        }

NullSafeSet
viene utilizzato invece per impostare i parametri da passare al Command quando NH esegue un'insert o un'update della proprietà sul database.
In questo caso passiamo a SQL la rappresentazione in stringa dell'oggetto di tipo SqlGeography ("POINT (lat lon)"). NH provvederà dunque ad eseguire una insert nel formato: INSERT INTO Table1 (id,Localization) VALUES (2,'POINT(lat lon)')

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

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

        public SqlType[] SqlTypes
        {
            get { return new[] {NHibernateUtil.String.SqlType}; }
        }
}

La proprietà SqlTypes è utile per indicare a NH quale tipo deve utilizzare nella creazione delle istruzioni DDL per la generazione dello schema.

Ora che la classe è pronta possiamo definirla nel file di mapping:

<property name="location" column="GeoLocation" type="MyNamespace.SqlGeographyUserType, MyNamespace.NhibernateMappings" />

Attenzione, nel caso vogliate che il vostro IUserType debba mappare più di una proprietà, e vogliate utilizzare quest'ultime nelle query HQL (ad esempio nel caso volessimo mappare singolarmente lat e lon e utilizzare nelle query HQL una sintassi del tipo localization.lat=...) allora è necessario che il vostro SqlGeographyUserType non erediti da IUserType ma da ICompositeUserType che aggiunge qualche metodo in più all'interfaccia IUserType.

venerdì 5 febbraio 2010

Eccomi qua!

Ciao a tutti,
questo è il primo post quindi è doverosa una presentazione.

Mi chiamo Daniele Natali, sono un consulente informatico e attualmente mi occupo della gestione di alcuni progetti presso il cliente per cui lavoro.
Ma oltre ad essere la mia fonte di guadagno, l'informatica è anche la mia passione. Perciò in questo blog troverete articoli, notizie legate al mondo informatico ed in genere a quello tecnologico :)

A tutti quelli a cui, come me, piacciono queste cose...a presto!!