.NET: gestione manuale della Garbage Collection

26 09 2008

Per chi arriva da linguaggi di ‘basso livello’, il Garbage Collector è una scoperta allo stesso tempo meravigliosa e sconvolgente, che (cito Wikipedia) porta ad un notevole cambio nello stile di programmazione dei linguaggi che lo implementano. Infatti non è più possibile richiedere esplicitamente di liberare la memoria utilizzata da un oggetto, ovvero terminare tale oggetto in modo deterministico, ma si lascia che il sistema esegua questa operazione automaticamente, nel momento in cui lo riterrà più opportuno per migliorare le prestazioni complessive (finalizzazione non deterministica).

Non quindi possibile, con un linguaggio ad ‘alto livello’, finalizzare un oggetto singolarmente ma è necessario porlo nella ‘raccolta di rifiuti’ e attendere che il sistema/compilatore si occupi di fare pulizia.

Può tuttavia essere necessario ‘forzare’ questa procedura di pulizia, anzichè lasciarla gestire automaticamente: un esempio potrebbe essere l’utilizzo di oggetti che incapsulano molte ‘risorse non gestite’ (ad esempio risorse del sistema operativo, quali file, finestre o connessioni di rete), quando l’applicazione non necessità più dell’oggetto, è certo che le risorse da esso utilizzate non saranno più necessarie. Ai fini delle prestazioni, può essere opportuno rilasciarle tutte insieme.

Per eseguire il ‘collect’ della GC, il Framework .NET fornisce, nella classe GC (Garbage Collection) il metodo GC.Collect, che (da notare bene) prima di eseguire una procedura di Garbage Collection sospende tutti i thread in esecuzione.

Tale funzionamento comporta infatti una riduzione delle prestazioni che può diventare problematica se si chiama GC.Collect più spesso del necessario. È opportuno non inserire codice che richiama GC.Collect in quei punti del programma a cui l’utente accede frequentemente. Tale pratica potrebbe infatti impedire al motore di ottimizzazione di determinare il momento migliore in cui eseguire una procedura di Garbage Collection, facendo decadere in maniera drastica le prestazioni dell’applicazione.





ASP.NET su un Network Load Balancing: problemi con il ViewState

3 03 2008

Una applicazione Asp.NET sviluppata inizialmente su un server singolo o su un cluster in Fault Tollerance, una volta passata su un ipotetico environment di produzione basato su un cluster Network Load Balancing, se non ben configurata puo’ restituire il seguente errore dopo un postback:

Validation of viewstate MAC failed. If this application is hosted by a Web Farm or cluster, ensure that <machineKey> configuration specifies the same validationKey and validation algorithm. AutoGenerate cannot be used in a cluster.

Il motivo e’ semplice da spiegare: il ViewState presente nella pagina (in pratica un campo ‘hidden’ inserito nel form della pagina, utilizzato dal framework per archiviare informazioni tra un postback e l’altro) viene criptato dal webserver, in modo da rendere non leggibili eventuali informazioni sensibili in esso contenute.

Nella configurazione di default, senza modifiche al Web.Config, la chiave utilizzata per la cifratura viene generata dinamicamente: in un ambiente Load Balancing (nel quale le richieste possono essere instradate indifferentemente su uno qualsiasi dei webserver presenti nel cluster) puo’ verificarsi che una pagina generata da un server (e quindi con una chiave di cifratura legata a quella macchina) effettui il postback su un’altro webserver che, trovando una ‘machinekey’ diversa dalla propria restuisce l’errore riportato sopra.

La soluzione e’ rapida e indolore: forzare nel web.config una chiave di cifratura (la cosidetta ‘MachineKey’) comune per tutti i nodi del cluster NBL.

La sintassi e’ la seguente (per i dettagli vi rimando alla pagina presente su MSDN):

<machinekey validationKey="AutoGenerate,IsolateApps" [String]
  decryptionKey="AutoGenerate,IsolateApps" [String]
  validation="SHA1" [SHA1 | MD5 | 3DES | AES]
  decryption="Auto" [Auto | DES | 3DES | AES]
/>

La generazione della MachineKey puo’ essere effettuata in maniera semplice e rapida da questo tool online.





ASP.NET: upload di files con progress bar

20 02 2008

Domanda di un lettore nei commenti del post sull’upload di files in asp.net:

simone (14:44:58)
ciao!,
sono alle prese anche io con l’upload di file su un server, pero in questo caso devo uploadare file di grossa dimensione, quindi vorrei creare una sorta di status bar per il file upload (che mi indicasse la percentuale di upload in tempo reale)…mi sai dare qualche consiglio?
grazie mlle per l’attenzione.

Oltre a rispondere al gentile Simone nei commenti, credo che sia utile mettere in evidenza la risposta in un post apposito.

Esiste un componente OpenSource per realizzare un sistema di upload molto completo, con progress bar visualizzabile sia all’interno della pagina che in un popup apposito: NeatUpload.

Ho avuto modo di utilizzarlo gia’ dalle primissime versioni: quella attuale e’ la 1.2.26 e noto con piacere che con l’autentare del numero di versione e’ aumentata anche la compatibilita’ con i vari browser esistenti e che l’interfaccia e’ stata aggiornata in maniera ‘ajax-oriented’ :-) (Demo)

L’utilizzo e’ semplicissimo: una volta inserito il componente sulla pagina, lo si puo’ instanziare e utilizzare come un normale controllo FileUpload. Il manuale e’ comunque molto esaustivo.





ASP.NET: accedere a un controllo presente su una MasterPage da una ContentPage

6 02 2008

aspnet.pngChi sviluppa in ASP.NET si sara’ sicuramente accorto di quanto sia importante tenere bene a mente il ciclo di vita di una pagina: durante la creazione delle pagina stessa si susseguono una serie di eventi in un ordine ben definito, e tutto va realizzato proprio nel rispetto di tale ordine.

Nel caso la nostra applicazioni utilizzi le MasterPages la faccenda si complica un pochino, e benche’ sia possibile includere codice nella MasterPage spesso ci si trova nella situazione di dover agire dal codice presente su una ContentPage su un controllo presente invece nella pagina Master.

La procedura che utilizzo di solito ‘ riassunta in questo esempio:

Dim MP_TextBox As TextBox = CType(Page.Master.FindControl("TextBoxMaster"), TextBox)
MP_TextBox.Text = "TESTO"

Il funzionamento e’ presto spiegato: tramite la funzione FindControl mi ricavo una istanza del controllo di tipo TextBox presente sulla masterPage.
Tale istanza mi viene restituita come oggetto ‘grezzo’, quindi ne effettuo il cast in un oggetto TextBox utilizzando CType. A questo punto posso accedere tranquillamente al controllo.Tale procedura puo’ essere utilizzata anche per metter mano sulla MasterPage a controlli non strettamente legati al framework.
Un esempio puo’ essere quello di modificare il background del body (dichiarato quindi nella MP).
Per prima cosa e’ necessario dichiarare il body della MP come runat=”server”, e assegnargli un ID:


<body topmargin=0 leftmargin=0 runat=server id="bodyMaster" >

in seguito, dalla content page il codice sara’ il seguente:


Dim MP_Body As System.Web.UI.HtmlControls.HtmlGenericControl = CType(Page.Master.FindControl("bodyMaster"), System.Web.UI.HtmlControls.HtmlGenericControl)
MP_Body.Attributes("bgcolor") = "red"

Ovviamente, dell’oggetto ottenuto tramite FindControl andra’ effettuato il casting in HtmlGenericControl.





Asp.Net: disabilitare l’azione del tasto Back del browser

17 01 2008

Erano un paio di giorni che io e il buon Gianpiero ci arrovellavamo per risolvere un problema apparentemente semplice: con asp.net e i suoi ‘postback’ l’uso da parte dell’utente del tasto back in alcuni casi risulta fatale al corretto funzionamento della web application.

Beh…io mi ero già arrampicato su una soluzione molto ’spaghetti-code’, con un controllo su variabili di sessione e viewstate, quando il guru si avvicina con un sorriso che dice ‘ho risolto!’.

Ebbene, la soluzione era più semplice del previsto, senza nemmeno una riga di codice ’server-side’:


<head runat=“server”>
<meta http-equiv=“Expires” CONTENT=“0″>
</meta><meta http-equiv=“Cache-Control” CONTENT=“no-cache”>
</meta><meta http-equiv=“Pragma” CONTENT=“no-cache”>
<script>
window.history.forward(1);
</script>
</meta></head>

In pratica, a ogni caricamento della pagina si viene ‘forwardati’ alla prima pagina della history: alla pressione dell’eventuale tasto ‘back’, si ritorna comiunque alla pagina dove ci si trovava, con la differenza che essendo tutte le informazioni memorizzate dal framework in variabili di sessione e viewstate i dati inseriti non vengono persi.

[via]





ASP.NET: realizzare lo screenshot di una pagina web

11 01 2008

Scenario: web application in Asp.NET per la gestione e pubblicazione di un piccolo magazine informativo ad uso interno.

Oltre al numero attuale, gli utenti possono accedere, da una apposita pagina di archivio, alle precedenti pubblicazioni.
Perche’ non realizzare qualcosa per creare in tempo reale uno screenshot del numero in archivio, ridurlo a piccolo thumbnail e affiancarlo alla descrizione del numero in elenco?

Di gia’ fatto non ho trovato niente, a parte un controllo ActiveX ‘commerciale’ e sinceramente molto poco economico, quindi ho deciso di realizzare qualcosa partendo da zero.

L’idea e’ questa: perche’ non utilizzare un controllo di tipo WebBrowser per caricare la pagina della quale vogliamo realizzare lo screenshot e poi ‘ritagliare’ il contenuto, salvando il tutto in una immagine?
Il tutto si puo’ fare anche in maniera non ‘visuale’, impacchettando tutto in una bella classe che automatizza la procedura: in questo modo l’oggetto puo’ essere usato indifferentemente in una web application o in una applicazione stand-alone.

Ho realizzato il tutto in C#: una volta impacchettato l’oggetto in una classlibrary ci si ritrova tra le mani un bel file .DLL da includere nella propria applicazione.

Di seguito il sorgente

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Reflection;

using WinHttp;

    public class WebSiteThumbnail
    {
        private string url = null;
        private Bitmap bmp = null;
        public Bitmap Image
        {
            get
            {
                return bmp;
            }
        }
        private ManualResetEvent mre = new ManualResetEvent(false);
        private int timeout = 5; // Da raffinare
        private int thumbWidth;
        private int thumbHeight;
        private int width;
        private int height;

        #region Metodi Statici
        public static Bitmap GetSiteThumbnail(string url, int width, int height, int thumbWidth, int thumbHeight)
        {
            WebSiteThumbnail thumb = new WebSiteThumbnail(url, width, height, thumbWidth, thumbHeight);
            Bitmap b = thumb.GetScreenShot();
            if (b == null)
                b = (Bitmap)System.Drawing.Image.FromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream("PAB.WebControls.Notavailable.jpg"));
            return b;
        }      

        #endregion

        #region Constructors
        public WebSiteThumbnail(string url, int width, int height, int thumbWidth, int thumbHeight)
        {
            this.url = url;
            this.width = width;
            this.height = height;
            this.thumbHeight = thumbHeight;
            this.thumbWidth = thumbWidth;
        }
        #endregion

        #region ScreenShot
        public Bitmap GetScreenShot()
        {
            Thread t = new Thread(new ThreadStart(_GetScreenShot));
            t.SetApartmentState(ApartmentState.STA);
            t.Start();
            mre.WaitOne();
            t.Abort();
            return bmp;
        }
        #endregion
        private void _GetScreenShot()
        {
            WebBrowser webBrowser = new WebBrowser();
            webBrowser.ScrollBarsEnabled = false;
            DateTime time = DateTime.Now;

            webBrowser.Navigate(url);

            webBrowser.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(WebBrowser_DocumentCompleted);
            while (true)
            {
                Thread.Sleep(0);
                TimeSpan elapsedTime = DateTime.Now - time;
                if (elapsedTime.Seconds >= timeout)
                {
                    mre.Set();
                }
                Application.DoEvents();
            }

        }
        private void WebBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            WebBrowser webBrowser = (WebBrowser)sender;
            webBrowser.ClientSize = new Size(this.width, this.height);
            webBrowser.ScrollBarsEnabled = false;
            bmp = new Bitmap(webBrowser.Bounds.Width, webBrowser.Bounds.Height);
            webBrowser.BringToFront();
            webBrowser.DrawToBitmap(bmp, webBrowser.Bounds);
            Image img = bmp.GetThumbnailImage(thumbWidth, thumbHeight, null, IntPtr.Zero);
            bmp = (Bitmap)img;
            webBrowser.Dispose();
            if (mre != null)
                mre.Set();
        }

        public void Dispose()
        {
            if (bmp != null) this.bmp.Dispose();
        }
    }

Veniamo a un esempio di utilizzo: una pagina .aspx che accetta come parametro l’url della pagina e restituisce uno stream con l’immagine della pagina.Realizzate quindi una nuova classlibrary, copiate il codice in GetSiteThumbnail.cs e compilate il tutto.Copiate poi la DLL generata nella directory ‘bin’ di un nuovo progetto web e realizzate un pagina chiamata (ad esempio) getsitethumb.aspx:

< %@ Import Namespace="System.Drawing" %>
< %@ Import Namespace="System" %>
< %@ Import Namespace="System.Web" %>
< %@ Import Namespace="WebSiteThumbnail" %>

< %@ Page MaintainScrollPositionOnPostback="false" %>
<html>
<script language="VB" runat="server">

  Sub Page_Load(Sender As Object, E As EventArgs)
        Dim immagine As System.Drawing.Bitmap
        If Request.QueryString("url" ) = "" Then Response.End()
        Dim thumb As New WebSiteThumbnail(Request.QueryString("url"), 715, 715, 100, 100)
        immagine = thumb.GetScreenShot()
        Response.ContentType = "image/jpeg"
        immagine.Save(Response.OutputStream, Imaging.ImageFormat.Jpeg)
        Response.End()
    End Sub
</script>
</html>

A questo punto, richiamando http://sito/getsitethumb.aspx?url=http://www.google.it visualizzerete sul browser un thumbnail di 1000×100px contenente una sezione di 715×715px dell’homepage di google.L’utilizzo all’interno di una normale pagina web e’ quindi il seguente:

<img src=”http://sito/getsitethumb.aspx?url=http://www.google.it”>





Visual Studio 2008, novità interessanti e alcuni dubbi

31 12 2007

bgvs.jpgDopo un paio di versioni beta, Microsoft ha finalmente rilasciato la versione definitiva di Visual Studio 2008, accompagnato dalla nuova versione del Framework .NET, la 3.5

Vediamo nel dettaglio le novità e partiamo subito con una mia perplessità a riguardo: una discreta parte delle nuove funzionalità dedicate allo sviluppo di applicazioni desktop sono orientate all’utilizzo con windows Vista e Office 2007.
Sostanzialmente, tutti i vantaggi derivati dall’utilizzo del nuovo ambiente di sviluppo per la realizzazione di applicazioni stand-alone potranno essere sfruttate solamente se il target di sviluppo è una piattaforma con le ultimissime versioni di Windows e Office.

VS2008 introduce infatti il supporto per Windows Presentation Foundation il sottosistema grafico di Windows Vista: utilizzando il WPF, la realizzazione dell’interfaccia utente in una applicazione avviene tramite la scrittura di codice XAML (eXtensible Application Markup Language), un linguaggio di markup basato su XML (nell’universo opensource il suo equivalente è XUL, derivato dal progetto Mozilla).

L’utilizzo del designer WPF integrato in VS2008 è molto simile a quello del vecchio ambiente di disegno dei Windows Forms, con in aggiunta la possibilità di lavorare in ‘SplitView’, con la finestra di design affiancata dal codice XAML relativo, come se si stesse sviluppando l’interfaccia di una web application: lo scopo finale a mio avviso è proprio quello di ridurre sempre di più le differenze tra lo sviluppo di applicazioni desktop e web, non per niente la stessa Microsoft sta spingendo per rendere XAML l’erede dell’ormai ‘vecchio’ HTML.

Incluso nella nuova versione di VS anche il supporto per lo sviluppo di applicazioni con Microsoft Office: compatibile con Office 2003 da tuttavia del suo meglio con il nuovo Office 2007, permettendo di sviluppare applicazioni native che sfruttano la user interface che caratterizza tale versione, come le ‘amate/odiate’ Ribbon Status Bar, Ribbon Bar e le Mini-Toolbar.

Il nuovo Framework 3.5 introduce anche la tecnologia Windows Communication Foundation, che in poche parole va a semplificare lo sviluppo delle applicazioni di rete unificando in una sola architettura tecnologie diverse come WebServices, Socket, Enterprice Service.
Una volta sviluppato un servizio WFC, da VS2008 è possibile effettuarne il debug senza realizzare una apposita applicazione Host, utilizzando due utility dedicate, il WFC Service Auto Host e il WFC Test Client.

Presente come estensione installabile in Visual Studio 2005, nella versione 2008 il supporto per Windows Workflow Foundation è presente nativamente.
WWF permette la progettazione e lo sviluppo di applicazioni legate a stati ben definiti: un esempio calzante lo si può trovare all’interno di un programma di contabilità. Lo stato di una fattura o di un ordine attraversa degli ’step’ ben definiti, utilizzando il WWF e il suo designer presente in VS2008 è possibile definire lo stato dei controlli presenti nell’interfaccia grafica legati a ogni singolo step.

L’accesso alle basi dati è stato rivoluzionato in questa nuova versione di Visual Studio dall’introduzione nel framework 3.5 del linguaggio LINQ (Language INtegrated Query).
Lo scopo di LINQ è di unificare il linguaggio di accesso ai dati: lo sviluppatore lo potrà utilizzare per scrivere query nativamente in VB o C# senza dover ricordare gli eventuali dialetti di SQL legati alla piattaforma di database utilizzata, trattando i dati presenti nel Db relazione come oggetti. Lontanamente è quello che in java si è iniziato a fare con Hibernate, seguito poi da molti ‘cloni’.

La realizzazione delle applicazioni web in VS2008 prende invece una piega interessante: .NET 3.5 porta con se infatti il supporto nativo per la programmazione AJAX, riallineando il framework Microsoft a molti altri presenti nel panorama OpenSource già dotati del supporto per ajax.
Con le nuove API è possibile sia sviluppare da zero una nuova applicazione AJAX, oppure migrare una precedentemente sviluppata alla nuova tecnologia utilizzando i controlli ScriptManager e UpdatePanel (vecchie conoscenze di chi ha iniziato in passato a ’spippolare’ con ATLAS e AJAX.NET).
Un’altra novità nell’ambiente di sviluppo è la presenza di completamento del codice, dell’intellisense e del debug per il codice Javascript ‘Client-Side’.

In definitiva il nuovo ambiente di sviluppo targato Microsoft a mio avviso è degno di uno sguardo piu approfondito, nonostante la fruizione di interessanti funzionalità sia limitata a clients Windows Vista+Office 2007.





Asp.Net: mantenere la posizione della pagina dopo un postback

21 11 2007

Come al solito l’ottimo Gianpiero riesce a stupirmi…me lo sono sempre domandato, e non ho mai avuto tempo/voglia di mettermi a spippolare sull’argomento:

Quando in Asp.net realizziamo un pagina abbastanza lunga, come fare ad evitare che dopo un postback la pagina stessa si riposizioni all’inizio, e non sul punto dove l’avevamo lasciata?

Ce lo spiega appunto Gianpiero in questo post.





Asp.NET e WebParts: errore di mancata deserializzazione dei personalization blobs

15 11 2007

Post abbastanza ’specifico’, magari puo’ far comodo a qualcouno che si e’ trovato nella mia stessa situazione. :-)

Lavorando con Asp.Net e le WebParts mi sono trovato davanti a un errore criptico e all’apparenza difficile da riprodurre: aggiungendo alla pagina una WebPart realizzata con uno UserControl, nel caso il controllo utilizzasse delle proprieta’ di tipo ‘Enum’ dichiarate direttamente nel codebehind saltuariamente la pagina non viene piu’ visualizzata, restituendo questo errore

Value cannot be null.
Parameter name: enumType
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.ArgumentNullException: Value cannot be null.
Parameter name: enumType

Dopo lunghi smazzamenti ho trovato la soluzione: a quanto pare, nel blob serializzato sul database contenente le configurazioni le informazioni sono legate all’ Assembly Name della pagina/controllo, anche i tipi di dati dichiarati direttamente nel sorgente del controllo.

Alla successiva ricompilazione l’Assembly Name cambia e il riferimento nel blob di configurazione non viene piu’ visto come valido, generando l’eccezione.

Per ovviare a questo problema e’ sufficiente dichiarare tutti i property types all’interno di una classe distinta, non nel codebehind della pagina o del controllo.





Asp.net: personalizzazione delle webparts a livello di pagina

17 10 2007

Grande cosa le webparts in Asp.net 2.0: la possibilita’ di offrire interfacce personalizzabili agli utenti, con la possibilita’ per ognuno di salvare le proprie impostazioni e’ una gran bella cosa.

Tuttavia ci si puo’ trovare in una situazione simile alla mia:

piccolo portale realizzato con le webpart, con lo storage delle personalizzazioni su un Ms SQLServer 2000; l’idea e’ quella di permettere la personalizzazione delle pagine tramite le webparts, ma tali dati devono essere legati non all’utente che le realizza, ma alla singola pagina.

Di default la classe SQLPersonalizationProvider prevede personalizzazioni legate esclusivamente all’utente che le realizza, come fare a fare in modo che vengano salvate usando come discriminante l’indirizzo della pagina?
Semplice, ‘illudendo’ il personalization provider che il nome della pagina sia lo username!

Tralasciando la realizzazione di una pagina funzionante con le webparts (si trovano in rete decine di tutorials,e cmq si presume che chi legga questo articolo quel passo lo abbia gia’ superato) passiamo alla realizzazione di una classe custom derivata da SQLPersonalizationProvider:


Imports Microsoft.VisualBasic

Public Class CustomSqlPersonalizationProvider
Inherits SqlPersonalizationProvider

Protected Overrides Sub LoadPersonalizationBlobs(ByVal _webPartManager As WebPartManager, ByVal _path As String, ByVal _userName As String, ByRef _sharedDataBlob() As Byte, ByRef _userDataBlob() As Byte)
Dim pagina As String
pagina = HttpContext.Current.Request.QueryString("page")
MyBase.LoadPersonalizationBlobs(_webPartManager, _path, pagina, _sharedDataBlob, _userDataBlob)
End Sub

Protected Overrides Sub ResetPersonalizationBlob(ByVal _webPartManager As WebPartManager, ByVal _path As String, ByVal _userName As String)
Dim pagina As String
pagina = HttpContext.Current.Request.QueryString("page")
MyBase.ResetPersonalizationBlob(_webPartManager, _path, pagina)
End Sub

Protected Overrides Sub SavePersonalizationBlob(ByVal _webPartManager As WebPartManager, ByVal _path As String, ByVal _userName As String, ByVal _dataBlob() As Byte)
Dim pagina As String
pagina = HttpContext.Current.Request.QueryString("page")
MyBase.SavePersonalizationBlob(_webPartManager, _path, pagina, _dataBlob)
End Sub

End Class

Di cosa si tratta? La nuova classe ereditata da SQLPersonalizationProvider esegue l’override dei metodi dedicati al salvataggio, caricamento e reset delle impostazioni e li altera in modo da utilizzare come identificativo per i dati salvati sul DB il valore del parametro ‘page’ passato in querystring anziche lo username dell’utente collegato.

A questo punto, dobbiamo modificare il web.config in modo da far utilizzare questa nuova classe al posto della tradizionale:

<webparts>
<personalization defaultProvider="CustomSqlPersonalizationProvider">
<providers>
<add name="CustomSqlPersonalizationProvider"
type="CustomSqlPersonalizationProvider"
connectionStringName="SQLConnString"
applicationName="/" />

</providers>

<authorization>
<deny users="*" verbs="enterSharedScope" />
<allow users="*" verbs="modifyState" />
</authorization>
</personalization>
</webparts>

Passando quindi alla nostra pagina il parametro ‘page’ dalla querystring potremo differenziare le personalizzazioni:

le modifiche effettuate su http://sito/default.aspx?page=pagina1 saranno quindi separate e distinte da quelle su http://sito/default.aspx?page=pagina2 e cosi’ via.

interessante notare che con questo metodo la creazione della nuova pagina avviene come su un wiki: cercando infatti di accedere a una pagina attualmente non presente viene presentata una pagina con le webpartzones vuote, pronte per essere ‘riempite’.





ASP.NET: modificare a runtime campi in un DetailsView

9 10 2007

Sviluppando in Asp.Net puo’ saltare fuori la necessita’ di modificare a runtime un valore nelle comodissime DetailsView: ad esempio facendo in modo che, una volta passati in modalita’ ‘insert’ alcuni campi siano gia’ valorizzati.

Presupponendo quindi di avere nella nostra pagina un controllo DetailsView chiamato DetailsView1, andremo a modificare la textbox presente nella prima riga della stessa, inserendo il codice seguente in corrispondenza dell’evento PreRender :

Protected Sub DetailsView1_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles DetailsView1.PreRender
'Controllo che il DetailsView sia in modalita' 'insert'
If DetailsView1.CurrentMode = DetailsViewMode.Insert Then
'dichiaro un tipo [row] per effettuare la ricerca al'interno della riga
Dim [row] As DetailsViewRow
'Controllo che il DetailsVew contenga righe
If DetailsView1.Rows.Count > 0 Then
'Seleziono la riga che voglio modificare
[row] = DetailsView1.Rows.Item(1)
'Setto il campo al valore scelto
CType([row].Cells(1).Controls.Item(1), TextBox).Text = "ValoreModificato"
End If
End If
End Sub

p.s.
Dimenticavo! La realizzazione di questo frammento di codice sarebbe stata impossibile senza il provvidenziale aiuto di Gianpiero! :-)





ASP.NET: upload di foto con creazione di thumbnails

29 09 2007

Oggi vi propongo un piccolo frammento di codice in vb.net che permette di effettuare da una pagina aspx l’upload di una foto e la creazione del relativo thumbnail.

Per prima cosa, inseriamo sulla pagina un controllo FileUpload e un pulsante che darà il via al processo di upload:

<asp:FileUpload ID=”FileUpload1″ runat=”server” />
<asp:Button ID=”Button1″ runat=”server” Text=”Carica Immagine” />

in seguito nel CodeBehind relativo al click sul pulsante realizziamo l’upload e la creazione del ThumbNail:


Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
If Not FileUpload1.PostedFile Is Nothing And FileUpload1.PostedFile.ContentLength > 0 Then
Dim unixtime As String
unixtime = (DateTime.UtcNow - New DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds
unixtime = unixtime.Replace(",", "")
'Dim fn As String = System.IO.Path.GetFileName(FileUpload1.PostedFile.FileName)
Dim fn As String = unixtime &amp; "-" &amp; System.IO.Path.GetFileName(FileUpload1.PostedFile.FileName)
Dim SaveLocation As String = Server.MapPath("foto") &amp;  "\\\\" &amp;  fn

Try
FileUpload1.PostedFile.SaveAs(SaveLocation)
'Creazione Miniatura
Dim img As System.Drawing.Bitmap
Dim imgNew As System.Drawing.Image
Dim inp As New IntPtr()

img = img.FromFile(SaveLocation)
imgNew = img.GetThumbnailImage(100, 62, Nothing, inp)
imgNew.Save(Server.MapPath("foto") &amp; "\\\\thumbs\\\\" &amp; fn, System.Drawing.Imaging.ImageFormat.Jpeg)

'Codice per eventuale insert in database

Catch Exc As Exception
Response.Write("Error: " &amp; Exc.Message)
End Try
Else
Response.Write("Please select a file to upload.")
End If
End Sub

Finito! La foto originale verrà caricata nella drectory ‘foto’, mentre la miniatura realizzata utilizzando il metodo GetThumbnailImage (grazie a Gianpiero per l’esaustivo tutorial sull’utilizzo) verrà creata in ‘foto/thumbs’.





L’uovo di Colombo

15 12 2006

In questo post del granitico Gianpiero (cintura rossa di ASP.NET, 10° dan)  trovo una interessante soluzione a un problema che affligge da sempre i programmatori ASP e ASP.NET: la gestione delle date a prescindere dalla lingua utilizzata dal server web e dal database.





.NET Style and Naming Guidelines

9 10 2006

Recentemente mi sono trovato a lavorare con dei colleghi con lo scopo di redigere delle linee guida comuni per lo sviluppo in .NET.
Naturalmente come documentazione di partenza è stata usata quella presente su MSDN e sui blog di sviluppatori .NET.

Ecco un breve riassunto di quanto è uscito da questo ‘Brain Storming’ :-)

Style Guidelines

Le Style Guidelines puntano a regolamentare lo stile di scrittura del codice, in modo da rendere i listati il piu’ possibile leggibili.

Indentazione e tabulazioni

Non credo sia necessario ribadire l’importanza dell’indentazione nella redazione di codice leggibile.
L’indentazione standard e’ di 4 spazi.
Parecchi testi a riguardo sconsigliano l’utilizzo del tasto di tabulazione (0×09), in quanto il carattere non viene identificato allo stesso modo da tutti gli editor: nel nostro caso ci viene incontro il sistema di editing di Visual Studio che si occupa di convertire la pressione del tasto TAB in 4 spazi.

Parentesi

Nel caso di linguaggi che utilizzano le parentesi graffe {} per racchiudere blocchi di codice le parentesi di apertura devono trovarsi all’inizio della linea successiva al frammento di codice che apre il blocco; il codice contenuto nelle parentesi deve essere indentato di 4 spazi.

Es.

if (Qualcosa)
{
    FaiQualcosa();
}
else
{
    FaiQualcosaltro();
}

Commenti

I commenti dovrebbero essere usati per descrivere il maggior numero di informazioni utili alla comprensione da parte di terzi del codice che si va a realizzare.
Non ci sono linee guida riguardanti i commenti nella documentazione del .NET Framework, tuttavia sappiamo che e’ commentando seguendo lo standard XML possiamo rendere il nostro codice ‘parlante’:

///
/// Scriviamo una descrizione per la classe Class1
///
/// Un parametro!
/// Informazioni ad uso interno
public class Class1
{
    public Class1()
    {
        //
        // TODO: Add constructor logic here
        //
    }

A questo punto la classe Class1 e’ diventata ‘parlante’:

Questo standard dovrebbe essere rispettato per tutte le classi e i membri presenti in un progetto, allo scopo di rendere chiaro e fruibile il codice ad altri partecipanti allo sviluppo nella maniera piu’ rapida possibile.

Uso degli spazi

E’ possibile rendere il codice piu’ leggibile diminuendone la densita’ utilizzando i caratteri di spazio:

  • Usare uno spazio dopo una virgola usata per separare gli argomenti di una funzione
    Es.
    Console.In.Read(myChar, 0, 1);
  • NON usare spazi prima della parentesi di chiusura
    Es.
    Console.In.Read(myChar, 0, 1 );
  • NON usare spazi tra il nome della funzione e le parentesi
    Es.
    Console.In.Read (myChar, 0, 1);
  • Usare spazi prima dei controlli di flusso
    Es.
    while (x == y)
    if (x == y)

Naming Guidelines

La Coding Guidelines specifica le regole che devono essere applicate per scrivere il codice rispettando determinati criteri di sintassi e di forma.

La Naming Guidelines rappresenta quindi un sottoinsieme della Coding Guidelines e si focalizza nel descrivere le linee guida sulla nomenclatura dei membri(classi, interfacce, …)

La necessità di avere delle regole comuni sulle modalità di scrittura del codice non è una novità introdotta con i nuovi linguaggi del Framework .Net: ad esempio la Notazione Ungherese è stata ed è ancora oggi una delle pratiche più conosciute dalle comunità di sviluppatori.

Con l’introduzione del Framework .Net, Microsoft ha introdotto una nuova modalità di nomenclatura del codice, più adatta ai nuovi linguaggi(completamente object-oriented) e più adatta ai nuovi tools di sviluppo.

Naming Guidelines per librerie pubbliche

Si applicano :

  • per qualsiasi membro pubblicamente esposto
  • in generale per tutte quelle parti di codice che sono documentate

L’obiettivo è quello di fornire agli sviluppatori partecipanti un unica modalità di pubblicazione delle parti pubbliche di componenti e librerie in modo da garantirne la consistenza con il resto del progetto e del Framework .NET.

Naming Convention

Tre brevi concetti prima di iniziare con l’argomento:

  • Si definiscono Pascal Cased i nomi che hanno tutte le iniziali maiuscole.
    Es. RecuperaDatiCliente
  • Sono Camel Cased quelli che hanno l’iniziale della prima parola minuscola e le iniziali delle rimanenti parole maiuscole
    Es. getElementById
  • Nella notazione ungherese, un nome di variabile inizia con un prefisso costituito da una o più lettere minuscole in modo da formare un codice mnemonico per il tipo o lo scopo di questa variabile. Il prefisso è seguito dal nome scelto dal programmatore; quest’ultima parte è spesso indicata come il “nome assegnato”.
    Il primo carattere del nome assegnato è in maiuscolo per separarlo visivamente dal prefisso (quindi in Camel Case)
    Es.
  • nSize: numero intero
  • dwLightYears: double word
  • bBusy: boolean

Le linee guida da applicare sono :

  • Tutti i tipi e membri pubblici devono essere Pascal Cased (i membri “protected” fanno parte dei membri pubblici)

Es.

class MyClass {
public string ClassName { … } protected string GetClassName() { … }
}

  • I parametri devono essere Camel Cased

Es.

public void LoadData(int objectId) { … }

  • Gli acronimi sono gestiti All Cased se di 2 caratteri, Pascal Cased altrimenti


Es.

public int ID;
public string Html;

  • Non è permesso l’utilizzo della Notazione Ungherese

Es.

int iObjectId;
string strText;

Nella definizione dei nomi di metodi, eventi e proprietà con modificatori di accesso public, protected o internal :

  • Non usare gli underscores (_)
  • Non usare parole tutte maiuscole o tutte minuscole
  • Non usare la Notazione Ungherese

Usare i nomi al plurale per metodi e proprietà che ritornano collections o array

Es.

public MemberCollection GetMembers();
public Member GetMember()

Utilizzare il prefisso “Is”, “Has” o “Can” nel caso di metodi che restituiscono un boolean

Es.

public boolean IsMemberOfTeam();
public boolean HasRows();
public boolean CanWrite();

Identificatori dei controlli visuali

Su questo argomento è avvenuto il cosiddetto ’scisma degli sviluppatori’: sono saltate fuori 2 soluzioni, una basata sulla notazione ungherese e una orientata a sfruttare appieno le funzionalità di autocompletamento del codice presenti in Visual Studio 2005.

Come già detto, la prima scelta suggerisce l’utilizzo della ungherese, con i primi 3 (o 4) caratteri in minuscolo ad identificare il tipo di controllo.

Ecco un breve lista dei possibili prefissi da utilizzare:

Controllo Prefisso
Button btn
ComboBox cmb
CompareValidator cmv
CustomValidator ctv
DataGrid dgr
DateTimePicker dtp
DropDownList ddl
Form frm
Label lbl
LinkLabel lnk
Panel pnl
RequiredFieldValidator rfv
RegularExpressionValidator rxv
TextBox txt
GroupBox grp
Ecc… Ecc…

L’altra possibilità suggerisce la possibilità di usare un codice piu’ complesso per identificare i controlli visuali:

  • un prefisso di 2/3 lettere ad identificare il gruppo di controlli instanziati dall’utente (in questo caso ‘ux’)
  • il nome scelto per la variabile, in Pascal Case
  • Il nome del tipo di controllo creato, in Pascal Case

Esempio:
uxIndirizzoTextBox

In questo modo, in fase di scrittura del codice, basta digitare ‘ux’ e attivare l’autocompletamento per avere l’elenco dei controlli visuali presenti nel progetto; da li, aggiungendo le prime lettere del controllo possiamo completare la stringa con minori battute sulla tastiera.

Dopo lunghe disquisizioni, la scelta è caduta sulla seconda ipotesi. :-D

Class, Structs and Interfaces

Nella definizione di classi e interfacce :

  • Non usare prefissi per i nomi delle classi

Es.

// errato
public class CPersona {…}
// corretto
public class Persona {…}

  • Usare il prefisso “I” per i nomi delle interfacce

Es.

public interface IPersona {…}

  • Utilizzare un nome composto per le classi derivate, aggiungendo come suffisso il nome, o parte del nome, della classe base


Es.

public class PersoneCollection : CollectionBase {…}
public class CustomEventArgs : EventArgs {…}
public class CustomException : Exception {…}

Le classi non hanno bisogno di essere identificate da un prefisso in quanto rappresentano la struttura di default di ogni applicazione realizzata con il .Net Framework.

Per le interfacce è invece necessario utilizzare il prefisso “I” in modo da distinguerle dalle classi.

Denominazione degli Alias di namespace

Le Naming Guidelines documentate nel .NET Framework non suggeriscono se usare il Pascal Case o il Camel case con gli alias di namespace.

Nel C# tuttavia una delle funzioni dell’operatore :: è quella distinguere l’ambiguità che si può creare quando si definisce un Alias omonimo di un tipo, ad esempio:

using Sql = System.Data.Sql;
Class Sql {}

Sql::DataRow riga = null;

Questo suggerisce quindi che per evitare problemi inutili (collisioni tra nomi degli alias e dei tipi) è preferibile utilizzare il Camel case.

Naming Issues

E’ molto importante definire con accortezza anche i nomi che si vogliono assegnare ai membri pubblici di una classe che sarà esposta ed utilizzata da altri sviluppatori :

  • Usare nomi che abbiano un significato, che siano “parlanti”
  • Scegliere una lingua per identificatori e commenti
  • Evitare le abbreviazioni
  • Guardare se esistono nomi già utilizzati e conosciuti per quello che vogliamo descrivere

Es.
MyCollection.Count vs MyCollection.NumberOfElements (la proprietà Count è usata in framework diversi ed
è facilmente riconoscibile dallo sviluppatore, non
occore un nome nuovo,in questo esempio NumberOfElements
per specificare il concetto)

Fonte: Blog di Brad Abrams e sezione “Designing .Net Class Libraries” di MSDN.





ASP.NET AJAX

2 10 2006

E’ passato qualche mese da quando scrissi il primo post riguardante ATLAS, il framework sviluppato da Microsoft per aggiungere funzionalità asincrone ad ASP.NET.

Il progetto è progredito, ha perso il codename ATLAS per trasformarsi semplicemente in ASP.NET AJAX.

Nella HomePage del progetto è possibile visionare alcuni ‘videocorsi’ di sviluppo con asp.net ajax: nel primo viene messa in luce la semplicità di conversione di una normale applicazione asp.net a una basata su ajax.
Una volta creato il progetto utilizzando l’apposito template, si può sviluppare l’applicazione seguendo le direttive classiche di asp.net, salvo poi ‘racchiudere’ le parti che dovranno essere aggiornate in maniera asincrona all’interno di appositi tag.

Gli altri video riguardano problematiche e dimostrazioni piu’ specifiche: utile e interessannte la serie "How Do I".