tag:blogger.com,1999:blog-4036728005197938952024-03-19T04:41:23.643+01:00Il blog di Daniele NataliAnonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.comBlogger15125tag:blogger.com,1999:blog-403672800519793895.post-76949330384212264212013-06-12T15:48:00.001+02:002014-08-03T18:12:21.145+02:00Tripandia Points, dai valore ai tuoi viaggi!E' tempo di novità per <a href="http://www.tripandia.com/" target="_blank"><b>Tripandia</b></a>, da ieri sono arrivati i <b>Tripandia Points</b>!<br />
<br />
Vuoi sapere come funzionano?<br />
<br />
Ad ogni tuo racconto viene assegnato un punteggio che varia da 0 a 100, in base alla sua completezza. Più dettagli inserisci nel tuo racconto, più <b>Tripandia Points</b> gli verranno assegnati.<br />
Il calcolo avviene in tempo reale: ad ogni modifica al tuo racconto di viaggio, potrai vedere immediatamente i Tripandia Points accumulati.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
E come faccio a capire come incrementarli? Semplice, in ogni momento ti viene data un elenco di suggerimenti, permettendoti così di intervenire immediatamente sulle parti mancanti del tuo racconto.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Ma non è finita qui. I <b>Tripandia Points</b> dei tuoi viaggi concorrono a determinare il tuo punteggio personale, che puoi trovare nella pagina del tuo profilo, così come in tutte quelle degli altri viaggiatori di Tripandia.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
Dunque cosa aspetti? Corri a raccontare le tue esperienze di viaggio su <a href="http://www.tripandia.com/"><b>www.tripandia.com</b></a>. Da oggi è ancora più divertente con i <b>Tripandia Points</b>! <br />
<br />Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-10824561643064888142013-03-07T01:06:00.000+01:002014-08-03T18:13:38.495+02:00Tripandia è online!<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit4h9noVkWS30HUL9-Oyx1nZ0_lMEJ2f3caULNnMqkYsLXsQIevtHD6MrdIUyNGZlSWlJvMIQ0VZAjS1uLdogqqAsT4XdPcnUqnnohHUsVoLzsKar5WF3eGrc8QSFQ17leRfq84eGBUNI/s1600/1000_800.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit4h9noVkWS30HUL9-Oyx1nZ0_lMEJ2f3caULNnMqkYsLXsQIevtHD6MrdIUyNGZlSWlJvMIQ0VZAjS1uLdogqqAsT4XdPcnUqnnohHUsVoLzsKar5WF3eGrc8QSFQ17leRfq84eGBUNI/s1600/1000_800.png" height="320" width="400" /></a></div>
<br />
<br />
Finalmente, dopo ben un anno e quattro mesi di duro lavoro, il sito che ho sempre desiderato fare è online: <a href="http://www.tripandia.com/"><b>www.tripandia.com</b></a><br />
<br />
Di cosa si tratta? Beh partiamo dall'inizio...ormai circa quattro/cinque anni fa mi era venuta l'idea di creare un sito internet dedicato ai viaggiatori che, come me, erano alla disperata ricerca di informazioni prima della partenza dei loro <b>viaggi</b>.<br />
La motivazione era data dal fatto che, in quel momento, i siti che riportavano racconti di viaggio davvero utili erano ben pochi e tra l'altro realizzati molto male e dunque di poca utilità.<br />
Perché mi devo leggere un racconto di venti pagine, per poi capire che non è il viaggio adatto a me, quando mi basterebbero pochi elementi chiari per capire di che cosa si tratta?<br />
Perché pochissimi siti mi fanno vedere la mappa del viaggio prima che lo legga?<br />
E così tanti altri perché mi spingevano a volerne creare uno mio.<br />
<br />
Certo il progetto che avevo in mente non era banale, diciamo non era il sito che si sarebbe realizzato in qualche mese da solo alla sera. Perciò passano gli anni e il progetto rimane lì fermo nella mia testa.<br />
<br />
Ma quando un anno e mezzo fa il mio collega <a href="https://twitter.com/sundna78" target="_blank">Mauro </a>mi ha coinvolto nel suo desiderio di sperimentare qualcosa di nuovo, realizzare un progetto con tecnologie nuove, con le nuove idee provenienti dal web (nostra passione da sempre), la decisione è stata facile: realizziamo <b>Tripandia</b>!<br />
<br />
E così ci siamo...dopo aver tirato a bordo negli ultimi mesi anche <a href="https://twitter.com/tdj_" target="_blank">Antonio</a>, da circa una settimana il sito è online. Cosa è venuto fuori da questo lavoro? Beh sicuramente la cosa migliore è andare su <a href="http://www.tripandia.com/"><b>www.tripandia.com</b></a> e provarlo con i vostri occhi.<br />
Certo quella che vedrete è una prima versione e la strada è ancora lunga. Vi dico solo che il nostro mitico documento "Tripandia 2", dove annotiamo tutte le idee ancora da realizzare, ha una quantità di attività che sono maggiori di quello realizzato fino ad ora! E comunque non abbiate paura a darci ulteriori nuove idee...il file non ha limiti!!<br />
<br />
In sintesi Tripandia è un sito che raccoglie <b>racconti di viaggio</b>. Ma lo fa in modo nuovo, innanzitutto in modo strutturato, dando grande importanza all'itinerario effettuato, alle località visitate, e soprattutto ai compagni di viaggio! Ebbene si, Tripandia ha anche molte caratteristiche "<b>social</b>" (come avrebbe potuto non averle??), tu non sarai da solo a creare il tuo viaggio ma i tuoi <b>compagni di viaggio</b> possono aiutarti, aggiungendo i propri racconti, le proprie foto, altre località visitate che magari hai dimenticato.<br />
<br />
Beh non voglio svelare altro, altrimenti ti perderesti la sorpresa...quindi registrati subito su <a href="http://www.tripandia.com/"><b>www.tripandia.com</b></a> e inizia a raccontare i tuoi viaggi. Proverai personalmente che inserire un viaggio è anche divertente, oltre al fatto che, in questo modo, salverai per sempre i ricordi di quello che è per me una delle cose più belle della vita...viaggiare.<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-67818934911224236122012-02-14T01:07:00.001+01:002012-02-14T01:07:31.999+01:00Domain Model o View Model?Per quanti di voi hanno studiato e utilizzano ASP.NET MVC o comunque qualsiasi altro framework basato sul pattern <b>MVC</b>, saprà benissimo cosa si intende per <b>Model</b>, <b>View </b>e <b>Controller</b>.<br />
Bene, se per view e controller generalmente non si hanno dubbi nell'implementazione pratica, per il model è possibile farsi qualche domanda in più.<br />
<br />
Devo usare le mie classi di dominio? In molti libri di ASP.NET MVC o quasi in tutti gli esempi in giro per la rete, troverete la solita classe Product o User che viene utilizzata nel controller e passata tranquillamente alla view. Niente di più facile, no?<br />
<br />
Ma sarà sempre così? Beh la risposta è...quasi mai!<br />
Basta passare dall'esempio alla prima pagina "reale", per capire subito che le classi da utilizzare nella view non possono essere quelle di dominio. Ecco alcune motivazioni:<br />
<br />
<ul>
<li>E' quasi matematicamente certo che una pagina web non contenga dati di una solo classe di dominio. Banalmente se stai creando la pagina di edit del tuo caro oggetto Product e una delle sue proprietà deve essere selezionata da una tendina, dove passo alla view gli item dell'elenco?</li>
<li>Può capitare che esistano delle regole di validazione diverse a seconda della view in questione. Queste dunque non possono essere definite tramite il solo meccanismo di validazione tramite attributi delle classi di dominio</li>
<li>Capiterà facilmente che la view dovrà gestire solo alcune delle proprietà della classe di dominio. Perchè dunque dovrei fare il bind dei dati con l'intera classe Product, andando magari incontro a problemi di sicurezza nel caso ci fosserò proprietà che non possono essere editati dall'utente? Un utente malintenzionato potrebbe infatti iniettarvi anche queste proprietà in post. E' vero che si può sempre definire a livello di action quali proprietà far gestire o meno dal binder, ma è necessaria una configurazione in più e sopratutto ricordarsi di farla:)<br /> </li>
</ul>
<br />
In conclusione, difficilmente le vostre classi di dominio potranno essere passate direttamente alla view, bensì nella quasi totalità dei casi sarà necessario crearsi delle classi ad-hoc (<b>View Model</b>) con le sole informazione che interessano alla view e che vengono poi gestite da essa stessa.<br />
<br />
E' innegabile che ciò è un'attività dispendiosa in tempo, ma esistono alcuni progetti interessanti che possono venire in aiuto, come AutoMapper (<a href="http://automapper.org/" target="_blank">http://automapper.org</a>) che ti permette di mappare automaticamente le classi del View Model con quelle del Domain model.<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-56478370601499147702012-02-10T01:20:00.002+01:002012-02-10T01:21:10.075+01:00L'utilizzo della cache in NhibernateUna grande funzionalità che possiede <b>Nhibernate </b>è quella della possibilità di gestire e personalizzare l'utilizzo della cache.<br />
Per differenziarsi dalla cache che nhibernate utlizza nativamente (<b>cache di primo livello</b>), quella, la cui gestione è delegata a noi utilizzatori, è chiamata <b>cache di secondo livello</b>.<br />
<br />
Il suo utilizzo è davvero semplice.<br />
<br />
Ammettiamo di avere una tabella di configurazione nel nostro database (t_configuration), e di avere la relativa classe mappata (ConfigurationEntry)<br />
<br />
E' naturale che noi vorremmo cachare il contenuto di tale tabella, poichè probabilmente verrà modificata raramente e, allo stesso tempo,utilizzata di frequente.<br />
Quello che è necesario fare è aggiungere al file di mapping della classe il seguente tag:<br />
<br />
<pre class="programlisting"><cache
usage="read-write|nonstrict-read-write|read-only" <span class="co"></span>
region="RegionName" <span class="co"></span>
/></pre><br />
<u>usage</u><br />
<b>read-only</b>: nel caso i dati vengano sempre solo letti (è il nostro caso)<br />
<b>read-write</b>: nel caso i dati possano essere sia letti che scritti (controllare che il provider di caching scelto abbia il supporto al locking)<br />
<b>nonstrict-read-write</b>: nel caso i dati possano essere letti e raramente scritti. Dunque probabilmente è possibile trascurare casi di transazioni simultanee<br />
<br />
<u>region</u><br />
<b>RegionName</b>: nome della regione di cache (di default il nome della classe)<br />
<br />
Dunque nel nostro caso avremo un file di mapping simile al seguente:<br />
<br />
<pre class="programlisting"><?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping namespace="Test" assembly="Test" </pre><pre class="programlisting">xmlns="urn:nhibernate-mapping-2.2"></pre><pre class="programlisting"><class name="ConfigurationEntry" table="t_configuration" schema="dbo">
<b style="color: red;"> <cache usage="read-only"/></b>
<id name="Key" access="property" column="Key">
<generator class="assigned" />
</id>
<property name="Value" type="String" column="Value" length="50" /></pre><pre class="programlisting"></class>
</hibernate-mapping>
</pre>A questo punto se proviamo a eseguire per due volte consecutive la seguente funzione passandogli lo stesso parametro key<br />
<br />
<pre class="programlisting">public string GetValueByKey(string key)
{
return Session.Get<ConfigurationEntry>(key);
}
</pre><br />
potremmo notare che solo la prima volta viene eseguito l'accesso al database, mentre la seconda volta il valore viene prelevato direttamente dalla cache.<br />
<br />
Attenzione però che tale cache viene utilizzata solo per restituire le entità a partire dalla loro chiave. Nhibernate infatti ha messo in cache la coppia chiave-classe. Ma cosa accade quindi se io lancio per due volte consecutive la seguente funzione che mi restituisce tutti i record di configurazione?<br />
<br />
<br />
<pre class="programlisting">public List<ConfigEntry> GetAllConfigurationEntries()
{
return Session.CreateCriteria<ConfigurationEntry>()</pre><pre class="programlisting">.List<ConfigurationEntry>().ToList();
}
</pre><br />
Ebbene l'accesso al database viene effettuato in entrambi i casi. Questo perchè Nhibernate non ha messo in cache la query ma solo le coppie chiave-classe.<br />
<br />
Per tale motivo ci viene in aiuto la cosidetta <b>Query Cache</b>, che permette a Nhibernate di mettere in cache il risultato della query. La query cache viene attivata nel seguente modo:<br />
<br />
<pre class="programlisting">public List<ConfigEntry> GetAllConfigurationEntries()
{
return Session.CreateCriteria<ConfigurationEntry>()<b><span style="color: red;">.SetCacheable(true)</span></b></pre><pre class="programlisting"><b><span style="color: red;"> </span></b>.List<ConfigurationEntry>().ToList();
}</pre><br />
<br />
Ora eseguendo due volte la funzione, viene effettuato un solo accesso al database.<br />
Attenzione che la query cache mette in cache <b><u>solo le chiavi</u></b> del risultato della query. E' necessario dunque abilitare anche la cache di secondo livello alla classe in questione, come abbiamo visto prima. <br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-52953468278343409312012-02-07T15:27:00.000+01:002012-02-07T15:29:17.380+01:00Localizzazione e SEO di un sito multilingua<br />
Per chi di voi si è imbattuto nello sviluppo di un <b>sito internet multilingua</b>, probabilmente avrà capito che la gestione di più lingue non è un tema così facile da smarcare come potrebbe sembrare superficialmente. <br />
Se poi ci aggiungiamo il fatto che il sito da sviluppare è per il pubblico e la sua popolarità ne determina il successo, il tutto diviene più delicato e meritevole di riflessioni più approfondite.<br />
<br />
Riporto dunque qui le considerazioni da tenere a mente, con particolare attenzione alle problematiche <b>SEO</b>. Dobbiamo, infatti, avere la garanzia che il nostro sito sia indicizzato in TUTTE le lingue<b>.</b><br />
<br />
<ul>
<li><b>Evitare che il riconoscimento della lingua da utilizzare avvenga esclusivamente tramite la lettura della lingua del browser dell'utente, oppure tramite la localizzazione geografica dell'utente in base al suo IP</b>. Tale approccio è totalmente letale ai fini del SEO. Ricordiamoci infatti che il crawler di Google (e ovviamente anche degli altri motori di ricerca) si presenta al nostro sito con un IP e una lingua imprevedibile (nel maggiore dei casi US). Per i motori di ricerca esisterebbe perciò solo la versione statunitense del nostro sito.<br />
<br />
</li>
<li>Per risolvere tale problema è necessario <b>permettere al crawler di "cambiare lingua"</b>. Ciò può essere fatto prevedendo la presenza di link alle varie versioni localizzate del nostro sito. La presenza delle solite bandierine nazionali o di semplici link alle lingue disponibili, può essere una soluzione.<br />
<br />
</li>
<li>Garantire <b>un'associazione uno a uno tra url e pagina localizzata</b>. Come già detto dunque, evitare ragionamenti su cookie, lingua del browser, IP ecc.. tutti elementi estranei ai motori di ricerca. Questi strumenti vanno benissimo come "aiuti" da utilizzare al fine di reindirizzare automaticamente l'utente alla versione del sito con la sua lingua. <br />
<br />
</li>
<li><b>Evitare il problema di duplicazione del contenuto</b> tanto odiato dai motori di ricerca (due pagine diverse con lo stesso contenuto). Per tale motivo è necessario capire se si avrà a che fare con un sito che avrà contenuti diversi per ogni country (<b>sito localizzato</b>) oppure se il sito conterrà esclusivamente contenuti tradotti nelle varie lingue disponibili (<b>sito tradotto</b>).<br />
Tale differenziazione è fondamentale. Se infatti il vostro è il secondo caso, è necessario creare url che si differenziano solo per la lingua e non per la country. Ad esempio evitare pagine come www.tuosito.com/en/pagina.aspx e www.tuosito.com/en-US/pagina.aspx.<br />
In questo caso infatti le pagine sarebbero identiche (entrambe in inglese) e rompereste la relazione univoca tra url e contenuto della pagina.<br />Se questo è il vostro caso, la tendina delle bandiere nazionali deve essere sostituita con la scelta delle sole lingue disponibili.<br />
<br />
</li>
<li>Qual è l'organizzazione migliore per il mio sito multilingua? In pratica ci sono tre possibilità, ognuna che presenta vantaggi e svantaggi:<br />
<br />
<ul>
<li>Utilizzo di diversi <b>domini di primo livello (TLD)</b>: www.tuosito.com, www.tuosito.it, ecc...<br />
Soluzione da adottare solo per siti localizzati (non tradotti). Ha il vantaggio che il sito viene identificato come più "vicino" alla country, sia da parte dell'utente (psicologicamante preferisce vedere .it che .com o .org...), sia da parte dei motori di ricerca (il sito viene preferito nelle SERPs).<br />
Essendo ritenuti dai motori di ricerca come siti completamente diversi, i domini non soffrono di eventuali penalizzazioni in termini di ranking dovute ad errori nelle altre versioni localizzate.<br />
Per contro, tale soluzione richiede di dover registrare (e quindi pagare) più domini, dovendo affrontare anche le differenti gestioni burocratiche della registrazione del dominio nei vari stati.<br />Attenzione anche all'uso di cookie...sono siti diversi, dunque non possonio essere condivisi!<br /><br /></li>
<li>Utilizzo di diversi<b> sottodomini</b>: en.tuosito.com, it.tuosito.com, ecc...<br />Soluzione adatta sia nel caso di siti localizzati che tradotti. Essendo il dominio di primo livello uguale per tutte le versioni del sito, parte della "reputazione" viene ereditata dalle altre versioni localizzate.<br /><br /></li>
<li>Utilizzo di diverse <b>cartelle del sito</b>: www.tuosito.com/en, www.tuosito.com/it, ecc...<br />Essendo il dominio identico per tutte le versione, la "reputazione" viene completamente ereditata. Una penalizzazione del ranking di una versione condizionerebbe la popolarità di tutte le verioni localizzate.<br />Per tale motivo mi sento di sconsigliarla nel caso di siti localizzati, mentre può essere utilizzata con buona tranquillità nel caso di siti tradotti.<br />Tale soluzione inoltre è la più semplice da implementare e non richiede alcuna configurazione a livello di infrastruttura.</li>
</ul>
</li>
</ul>
Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-73947586832063340122011-12-03T17:12:00.001+01:002011-12-16T10:11:48.652+01:00Disponibile Mutuo Mobile Pro!Eccomi finalmente dopo lungo tempo a scrivere dell'uscita di Mutuo Mobile Pro, la versione a pagamento (molto esiguo direi!) di Mutuo mobile Free.<br />
<br />
La grande funzionalità che ha in più del fratello gratuito è la possibilità di simulare le rate del mutuo utilizzando gli indici del passato.<br />
<br />
Chi di voi si è mai imbattuto nella stipulazione di un mutuo, sicuramente avrà ricevuto dalle banche decine di tipologie diverse di mutuo. Ci sono quelle con il tasso fisso, quelle con il tasso variabile puro, quelle con il tetto massimo (CAP), quelle con tasso misto che vi permettono di cambiare durante il periodo del mutuo, ecc...<br />
Ecco che a questo punto, come è successo a me, non potete che essere leggermente disorientati.<br />
A questo punto, non avendo i poteri di veggenza per prevedere il futuro, mi sono chiesto: "perchè non farmi aiutare dal passato?". Quale sarebeb la scelta migliore se il futuro avesse lo stesso andamento del passato?<br />
<br />
Ed ecco che nasce Mutuo Mobile Pro...<br />
<br />
Per ogni mutuo potete configurare fino a 5 simulazioni contemporanee mettendole poi anche a confronto in un grafico.<br />
La configurazione di ogni simulazione consiste nell'inserimento di una (caso più comune) o più condizioni economiche (nel caso di mutui misti). <br />
<br />
Alcuni esempi:<br />
<br />
<br />
Simulazione 1: Tasso variabile basato su Euribor 1 mese + 1,2% di spread<br />
Simulazione 2: Tasso variabile con CAP basato su BCE + 1,7% di spread e tetto al 5,5%<br />
Simulazione 3: Tasso variabile basato su Euribor 1 mese + 1,5% di spread per 3 anni, poi tasso fisso al 4,5%<br />
<br />
Una volta configurate le simulazioni, le si elaborano e in qualche secondo hai il totale del mutuo e degli interessi pagati per ogni simulazione oltre al dettaglio delle rate da pagare! Comodo, no?<br />
<br />
Video dimostrativo dell'applicazione: <br />
<br />
<div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='420' height='266' src='https://www.youtube.com/embed/UXFhgaFTCT4?feature=player_embedded' frameborder='0'></iframe></div><br />
<br />
<br />
<div style="text-align: center;"><a href="https://market.android.com/details?id=daninat.mutuomobilepro" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;" title="Scarica Mutuo Mobile Pro"><img border="0" height="100px" src="http://2.bp.blogspot.com/-BWyH_6fBWS0/TtpThkgW6xI/AAAAAAAAAGw/QCVR6-jOVGw/s1600/mm_180x120.png" width="180px" /></a></div><br />
<a href="https://market.android.com/details?id=daninat.mutuomobilepro" target="_blank" alt="Scarica Mutuo Mobile Pro">Scaricala subito!</a><br />
<br />
<br />
<br />
<br />
<br />
<br />Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-73917109444524456582011-01-05T01:03:00.003+01:002011-01-05T11:36:42.620+01:00Come utilizzare le animazioni in Android persistendo lo stato finaleDurante lo sviluppo della mia prima applicazione Android, mi sono scontrato con l'utilizzo delle classi dell'SDK riguardanti le animazioni.<br>
<br>
Queste sono le classi principali da utilizzare:<br>
<ul>
<li><b>AlphaAnimation </b>nel caso si voglia agire sull'alpha channel di una view</li>
<li><b>RotateAnimation </b>se si desidera ruotare una view</li>
<li><b>ScaleAnimation </b>nel caso si voglia cambiare le dimensioni di una view </li>
<li><b>TranslateAnimation </b>se si vuole spostare una view </li>
<li><b>AnimationSet </b>è la classe che contiene più Animation (classe base delle classe appena citate) così da poter essere avviate contemporaneamente o con offset personalizzabili.</li>
</ul>
<ul>
</ul>
<br>
<a href="http://daninatblog.blogspot.com/2011/01/come-utiizzare-le-animazioni-in-android.html#more">Continua a leggere...»</a>Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com1tag:blogger.com,1999:blog-403672800519793895.post-1949525948455411372010-12-23T01:24:00.005+01:002014-06-06T16:43:09.656+02:00E' disponibile Mutuo Mobile!Eccomi qui dopo una lunga lunghissima assenza.<br />
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ù...<b>Android</b>!<br />
<br />
Da Maggio sono in possesso di un nuovo (beh nuovo, ormai è da ritenersi vecchio, mannaggia!) smartphone Android, l'HTC Desire.<br />
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?<br />
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 :)<br />
<br />
Ed ecco arrivato il giorno in cui sono riuscito a pubblicare la mia prima (piccola!) applicazione: <b>Mutuo Mobile. </b><br />
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.<br />
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.<br />
Non potevo esimermi dall'inserire il calcolo del piano di ammortamento con la possibilità di esportarlo in CSV e di inviarselo per posta.<br />
<br />
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.<br />
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 :)<br />
<br />
Che ne dite? :)<br />
<br />
Non mi resta che darvi il link al market dove potete provarla e darmi un feedback : <br />
<br />
<a href="https://play.google.com/store/search?q=pub:Daniele Natali"><br />
<img alt="Android app on Google Play"
src="http://developer.android.com/images/brand/en_app_rgb_wo_60.png" /><br />
</a><br />
<br />
<div class="appbrain-app" id="app356"><a href="http://www.appbrain.com/app/mutuo-mobile-free/daninat.mutuomobile" style="color: #555555; font-family: arial, sans-serif; font-size: 11px;">Mutuo Mobile Free for Android on AppBrain</a><script language="javascript" src="http://www.appbrain.com/api/api.nocache.js" type="text/javascript"></script></div><br />
A presto!Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-12836077843125529032010-03-12T00:37:00.000+01:002010-12-28T00:37:38.599+01:00Addin di Outlook per l'integrazione con TFSVi voglio segnalare un ottimo plugin per <span style="font-weight: bold;">Outlook </span>che offre alcune utili funzionalità di integrazione con <span style="font-weight: bold;">Team Foundation</span>.<br />
<br />
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 <span style="font-style: italic;">field </span>che avete impostato a livello di configurazione, così come l'oggetto della mail.<br />
Ciò può tornarvi molto utile ,ad esempio, nella creazione di work item di tipo <span style="font-style: italic;">Bug</span>, la quale è spesso conseguenza di una segnalazione via mail. Con questo addin, quindi, potrete a partire dalla mail inserire il <span style="font-style: italic;">Bug </span>in <span style="font-weight: bold;">TFS </span>e monitorare in futuro il suo stato.<br />
<br />
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.<br />
<br />
Ecco i link per scaricare l'addin:<br />
<br />
<a href="http://blogs.microsoft.co.il/files/folders/leon/entry7654.aspx">Addin per Outlook 2003</a><br />
<br />
<a href="http://blogs.microsoft.co.il/files/folders/leon/entry9509.aspx">Addin per outlook 2007</a>Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-23251822016812685032010-03-11T00:36:00.000+01:002010-12-28T00:36:35.473+01:00Modificare un Work Item Type in Team FoundationLavorando con <b>Team Foundation</b> 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.<br />
A questo proposito vi descrivo come è possibile cambiare o aggiungere un <b>Work Item Type</b>, che è di fatto uno dei componenti principali di tutto il modello di gestione progettuale (che in TFS viene chiamato <b>Process Template</b>).<br />
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:<br />
<a href="http://msdn.microsoft.com/en-us/teamsystem/bb980963.aspx">Power Tools for Visual Studio 2008</a><br />
Nel caso volessimo modificare un Work Item Type già esistente è necessario aprire il menu di Visual Studio<i> Tools/Process Editor/Work Items Type/Open WIT from Server</i>, scegliere un progetto di TFS e un work item type.<br />
A questo punto il work item type (<b>WIT</b>) viene aperto in editing. Nel primo tab <i>Fields</i> è 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 <i>Fields</i> premere <span style="font-style: italic;">New </span>e inserire i dati del nuovo campo:<br />
<br />
<img src="http://blogs.dotnethell.it/filestore/8164_new%20field1.jpg" /><br />
<br />
Nel tab <i>Rules, invece, </i>è 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 <b>ALLOWEDVALUES</b>:<br />
<br />
<img src="http://blogs.dotnethell.it/filestore/8165_new%20field2.jpg" /><br />
<br />
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 <i>Layout</i> 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:<br />
<br />
<img src="http://blogs.dotnethell.it/filestore/8166_new%20field3.jpg" /><br />
<br />
Prememdo su <i>Preview Form</i> è possibile avere l'anteprima del form modificato:<br />
<br />
<img src="http://blogs.dotnethell.it/filestore/8167_new%20field4.jpg" /><br />
<br />
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ù <i>Tools/Process Editor/Work Items Type/Export WIT</i>), eseguire le modifiche e importare il file nuovamente in TFS (menù <i>Tools/Process Editor/Work Items Type/Import WIT</i>).Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-1451716548895655152010-03-03T00:35:00.000+01:002010-12-28T00:35:28.935+01:00Mantenere lo stato dei controlli in ASP.NET MVCUna delle prime cose che si imparano quando si affronta per la prima volta il mondo di <span style="font-weight: bold;">ASP.NET MVC</span> provenendo da un'esperienza di WebForms, è che la grande "invenzione" che ha reso tanto popolare quest'ultima tecnologia non esiste più, ossia il <span style="font-style: italic;">ViewState</span>.<br />
<br />
Dopo un momento di smarrimento, si cerca di capire come farne a meno (e capirete che di vantaggi ce ne sono!)<br />
<br />
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..)<br />
<br />
Prendiamo ad esempio una textbox:<br />
<br />
<%= Html.TextBox("SearchText","testo di default")%><br />
<br />
e il seguente Action method invocato dal submit presente nella pagina della textbox:<br />
<br />
[AcceptVerbs(HttpVerbs.Post)]<br />
public ActionResult Search(string SearchText)<br />
{<br />
return View();<br />
}<br />
<br />
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!<br />
<br />
Cosa è successo?? <br />
<br />
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:<br />
<br />
1 MVC controlla se è presente un valore nella proprietà <span style="font-weight: bold;">ModelState["SearchText"].Value.AttemptedValue</span>. 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.<br />
<br />
2 MVC controlla se è presente un <span style="font-weight: bold;">valore di default</span> del controllo (nel nostro caso la stringa "testo di default")<br />
<br />
3 MVC controlla se è presente un valore per l'elemento <span style="font-weight: bold;">ViewData["SearchText"]</span>. ViewData è la collection che il Controller passa alla View con tutti i dati sul modello.<br />
<br />
4 MVC controlla se è presente un valore per l'elemento <span style="font-weight: bold;">ViewData.Model.SearchText </span>. ViewData.Model è l'oggetto che viene passato dal Controller alla View come modello<br />
<br />
Tornando al nostro caso, al primo accesso della pagina scatta la regola 2. Il ModelState, infatti, è vuoto mentre la stringa di default è valorizzata.<br />
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).<br />
<br />
E' bene dunque tenere sempre a mente queste priorità in modo da non incorrere in comportamenti non previsti.Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-74954605221838598982010-02-18T00:34:00.000+01:002012-02-14T09:46:59.918+01:00Filtrare i dati in SSAS con la funzione UserName()<style>
@font-face {
font-family: "Cambria Math";
}@font-face {
font-family: "Calibri";
}p.MsoNormal, li.MsoNormal, div.MsoNormal { margin: 0cm 0cm 10pt; line-height: 115%; font-size: 11pt; font-family: "Calibri","sans-serif"; }.MsoChpDefault { }.MsoPapDefault { margin-bottom: 10pt; line-height: 115%; }div.Section1 { page: Section1; }
</style> 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.<br />
<br />
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.<br />
<br />
Imbattendomi nella configurazione dei permessi di SSAS ho trovato la seguente soluzione che utilizza la funzione <span style="font-weight: bold;">UserName()</span> che restituisce l'utente corrente:<br />
<br />
- Innanzitutto è necessario creare un Ruolo ad hoc all'interno del cubo a cui assegnare tutti gli utenti che devono essere soggetti a tale comportamento.<br />
<br />
- 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 <span style="font-style: italic; font-weight: bold;">Dimension Data</span> nella sezione <span style="font-style: italic; font-weight: bold;">Allowed member set</span> la seguente espressione MDX :<br />
<br />
DESCENDANTS( [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]") )<br />
<br />
- 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 <span style="font-style: italic; font-weight: bold;">CellData </span>nella sezione<span style="font-style: italic;"> <span style="font-weight: bold;">Allow reading of cube content</span></span> la seguente espressione MDX:<br />
<br />
ISANCESTOR( [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]"),[UtentiDIM].[Gerarchia].CURRENTMEMBER )<br />
OR [UtentiDIM].[Gerarchia].members("[UtentiDIM].[Gerarchia].["+UserName()+"]") IS [UtentiDIM].[Gerarchia].CURRENTMEMBER<br />
<br />
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.Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-62402885884975914382010-02-16T00:31:00.000+01:002010-12-28T00:32:08.943+01:00Unit test del Data Layer con TransactionScopeNel momento in cui si deve eseguire uno <span style="font-style: italic; font-weight: bold;">Unit Test</span> per testare il funzionamento delle classi per l'accesso ai dati, si possono percorrere due strade: <br />
<br />
- 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.<br />
<br />
- la seconda è quella invece di utilizzare un vero e proprio database di test.<br />
<br />
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.<br />
<br />
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.<br />
A tal fine torna molto utile l'utilizzo della classe <span style="font-weight: bold;">TransactionScope (assembly System.Transaction)</span>, 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.<br />
<br />
Nell'esempio utilizzo <span style="font-weight: bold;">NUnit </span>come framework per lo unit test, ma il concetto è valido ovviamente per tutti i framework.<br />
<br />
protected TransactionScope _transactionScope;<br />
<br />
[SetUp]<br />
public void Setup()<br />
{<br />
_transactionScope = new TransactionScope();<br />
}<br />
<br />
[TearDown]<br />
public void TearDown()<br />
{<br />
_transactionScope.Dispose();<br />
}<br />
<br />
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<span style="font-weight: bold;"> Data Layer</span>Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-92101865134475930412010-02-11T00:29:00.000+01:002010-12-28T00:38:08.101+01:00Nhibernate custom type mapping e dati geospazialiOrmai è da un pò di tempo che utilizzo Nhibernate nei progetti che seguo e ogni tanto capita di dover affrontare nuove esigenze. <br />
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.<br />
<br />
A titolo di esempio riporto il caso in cui una classe abbia una proprietà di tipo <span style="font-style: italic;">SqlGeography</span>. La classe <span style="font-style: italic;">SqlGeography </span>è contenuta nell'assembly Microsoft.SqlServer.Types ed è la classe che rappresenta il data type <span style="font-style: italic;">Geography </span>di SQL Server 2008 non direttamente supportato da NHibernate.<br />
Il data type <span style="font-style: italic;">Geography </span>è la rappresentazione binaria delle coordinate geografiche di un punto sulla terra (coppia latitudine longitudine).<br />
Quello che vogliamo ottenere è il mapping della proprietà di tipo SqlGeography e il campo sul database di tipo <span style="font-style: italic;">Geography </span>(rappresentazione binaria del testo "POINT (latitudine longitudine)")<br />
<br />
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:<br />
<br />
public class SqlGeographyUserType : IUserType<br />
{<br />
public object DeepCopy(object value)<br />
{<br />
if (value == null)<br />
return null;<br />
var sourceTarget = (SqlGeography)value;<br />
SqlGeography targetGeography = SqlGeography.Point(sourceTarget.Lat.Value, sourceTarget.Long.Value,<br />
sourceTarget.STSrid.Value);<br />
return targetGeography;<br />
}<br />
<br />
public object Assemble(object cached, object owner)<br />
{<br />
return DeepCopy(cached);<br />
}<br />
public object Disassemble(object value)<br />
{<br />
return DeepCopy(value);<br />
}<br />
<br />
<span style="font-weight: bold;">Assemble </span>e <span style="font-weight: bold;">Disassemble</span> 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 <span style="font-weight: bold;">DeepCopy</span>.<br />
<br />
public bool Equals(object x, object y)<br />
{<br />
if (ReferenceEquals(x, y))<br />
return true;<br />
if (x == null || y == null)<br />
return false;<br />
return x.Equals(y);<br />
}<br />
<br />
Il metodo <span style="font-weight: bold;">Equals</span> è utilizzato da NH per verificare se la proprietà è diversa da quella memorizzata nella snapshot salvata in memoria, così da provvedere al salvataggio su database.<br />
<br />
public int GetHashCode(object x)<br />
{<br />
return x.GetHashCode();<br />
}<br />
<br />
public bool IsMutable<br />
{<br />
get { return true; }<br />
}<br />
<br />
<span style="font-weight: bold;">IsMutable </span>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.<br />
<br />
public object NullSafeGet(IDataReader rs, string[] names, object owner)<br />
{<br />
object prop1 = NHibernateUtil.String.NullSafeGet(rs, names[0]);<br />
if (prop1 == null)<br />
return null;<br />
SqlGeography geo = SqlGeography.Parse(new SqlString(prop1.ToString()));<br />
<br />
return geo;<br />
}<br />
<br />
<span style="font-weight: bold;">NullSafeGet </span>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.<br />
<span style="font-weight: bold;"></span><br />
public void NullSafeSet(IDbCommand cmd, object value, int index)<br />
{<br />
if (value == null)<br />
((IDataParameter) cmd.Parameters[index]).Value = DBNull.Value;<br />
else<br />
((IDataParameter) cmd.Parameters[index]).Value = ((SqlGeography) value).STAsText().Value;<br />
;<br />
}<br />
<span style="font-weight: bold;"><br />
NullSafeSet</span> viene utilizzato invece per impostare i parametri da passare al Command quando NH esegue un'insert o un'update della proprietà sul database.<br />
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)')<br />
<br />
<span style="font-weight: bold;"></span> public object Replace(object original, object target, object owner)<br />
{<br />
return DeepCopy(original);<br />
}<br />
<br />
public Type ReturnedType<br />
{<br />
get { return typeof (SqlGeography); }<br />
}<br />
<br />
public SqlType[] SqlTypes<br />
{<br />
get { return new[] {NHibernateUtil.String.SqlType}; }<br />
}<br />
}<br />
<br />
La proprietà <span style="font-weight: bold;">SqlTypes</span> è utile per indicare a NH quale tipo deve utilizzare nella creazione delle istruzioni DDL per la generazione dello schema.<br />
<br />
Ora che la classe è pronta possiamo definirla nel file di mapping:<br />
<br />
<property name="location" column="GeoLocation" type="MyNamespace.<span style="font-weight: bold;">SqlGeographyUserType</span>, MyNamespace.NhibernateMappings" /><br />
<br />
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.Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0tag:blogger.com,1999:blog-403672800519793895.post-72222406401233621202010-02-05T23:58:00.000+01:002010-12-28T00:30:52.225+01:00Eccomi qua!Ciao a tutti,<br />
questo è il primo post quindi è doverosa una presentazione.<br />
<br />
Mi chiamo Daniele Natali, sono un consulente informatico e attualmente mi occupo della gestione di alcuni progetti presso il cliente per cui lavoro.<br />
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 :)<br />
<br />
A tutti quelli a cui, come me, piacciono queste cose...a presto!!Anonymoushttp://www.blogger.com/profile/05876457206203902617noreply@blogger.com0