Differenze tra Web Site e Web Application Project in Visual Studio

9 marzo 2011

Secondo me è fondamentale capire quali siano le reali differenze tra questi due tipi di progetti, io personalmente sono abituato ad utilizzare sempre e solo Web Application Project ma mi è capitato di lavorare su progetti in cui è stato utilizzato un “Web Site” (guarda caso proprio un progetto in Team). Conoscerne le differenze è un buon punto di partenza per poter capire quale delle due soluzione è più giusto scegliere in base al contesto.

Web Site

Un progetto “Web Site” non è altro che un insieme di file raggruppati all’interno di cartelle e sotto cartelle il cui tipo varia dalla semplice immagine, al file di testo per finire con i file di codice dell’applicazione. Non esiste un progetto reale associato ma una cartella root in cui tutto è contenuto. Un progetto di questo tipo è compilato dinamicamente mentre l’applicazione è in esecuzione e non contiene al suo interno una cartella bin.

Vantaggi:

  • non è necessario avere un file di progetto o una cartella virtuale per la sua esecuzione
  • il sito può essere facilmente distribuito facendo un copia incolla dei file da cui è composto

Svantaggi:

  • non essendoci il file di progetto non è possibile avere un file di questo tipo con cui, facendo doppio click, è possibile aprire il progetto ottenendo l’elenco completo di tutto il materiale da cui è composto.
  • per DEFAULT tutti i file che sono presenti all’interno della cartella del progetto sono da considerare parte stessa di questo, per tale motivo, se alcuni di questi vogliamo escluderli, dobbiamo inserire un suffisso per i file in questione: .exclude
Web Application Project

Questo tipo di progetto è molto più con un semplice “Web Site”. Pensato più in la nel tempo rispetto al suo predecessore, è considerato come un Add On di Visual Studio, aggiunto per la prima volta a partire da VS 2005, viene predisposto anche nei nuovi ambienti (Visual Studio 2008 e 2010).

Vantaggi:

  • Tutto il codice viene compilato in un singolo Assembly il cui contenuto viene piazzato all’interno della cartella bin.
  • Escludere file che non servono direttamente nel progetto è più semplice, essendoci una soluzione ed un file di progetto che ne identifica la struttura.

Svantaggi:

  • Condividere il lavoro con altri colleghi risulta più complesso nel caso si utilizzi Microsoft Visual Source Safe e roba simile.

Chiariti questi macro concetti diventa più facile decidere, in ogni caso la documentazione MSDN mette a disposizione molta più roba in merito, il mio articolo vuole essere un riassunto molto “short” di ambedue le possibilità. A voi la scelta developer!

.Net – Windows Form Applications & Context Sensitive Help

2 marzo 2011

Esistono diverse strategie con cui è possibile realizzare un Help che realmente possa essere utile. Il .Net Framework, per le applicazioni “Windows Form” mette a disposizione una serie di strumenti che è possibile utilizzare a proprio piacimento per la realizzazione di una documentazione in linea, tema la cui importanza non va assolutamente sottovalutata perché un buon software è sempre corredato da una buona documentazione.

Oggi voglio mostrarvi come ho realizzato una guida in linea sensibile al contesto (Context Sensitive Help) all’interno di una Client Application realizzata con il .Net Framework.

* Tralasciando la spiegazione di come si realizza una guida in linea (Microsoft Compiled HTML Help), argomento su cui ci sarebbe molto da discutere, questo post vuole spiegare come integrare un file di questo tipo all’interno di un’applicazione Windows Form.

Partiamo dai presupposti, un oggetto di tipo System.Windows.Forms.Form espone una serie di proprietà, tra queste troviamo anche un evento chiamato HelpRequested che lo sviluppatore può eventualmente implementare, a riguardo la documentazione parla chiaro:

L’evento HelpRequested viene solitamente generato quando l’utente preme il tasto F1 o fa clic su un pulsante della Guida sensibile al contesto.

L’implementazione risulta molto semplice, nei form in cui vogliamo fornire aiuto possiamo decidere di invocare un metodo che possa, in base al contesto, darci informazioni utili reperite direttamente dal file di guida.

Inoltre è necessario sapere che un file di guida “Microsoft Compiled HTML Help” contiene al suo interno una serie di file html compressi, questi file vengono associati ad un identificatore univoco definito in un file Header (scritto in C) e che riporta il riferimento ad ogni link, questo file viene utilizzato in fase di compilazione della guida e serve propri per indicizzare i contenuti della nostra documentazione per poterla successivamente interrogare utilizzando un TopicId.

Ora, all’interno dei Windows Form sono si presenti delle proprietà per la gestione dell’Help ma non sono quelle di cui noi abbiamo bisogno (in pratica il .Net Framework mette a disposizione una serie di Tooltip che vengono visualizzati per dare aiuto). Supponiamo di aver definito nel nostro file di guida un argomento chiamato “Come effettuare il login”, a questo è stato assegnato l’indice 1000, come fare per ottenere un focus su questo argomento nella guida in linea direttamente dall’applicazione? La soluzione che ho applicato è molto semplice, ogni controllo, all’interno di un applicazione Windows espone una proprietà chiamata Tag che è di tipo Object, lo scopo di questa è proprio memorizzare informazioni aggiuntive nel nostro form come per esempio oggetti, stringhe o quanto ci possa tornare più utile, io ho deciso di inserire in questo campo il TopicId che dovrebbe essere richiamato se l’utente chiede aiuto durante l’esecuzione dell’applicazione, successivamente ho realizzato un oggetto che ho chiamato HelpProvider il cui compito è attivare l’help in linea su un determinato form:

internal sealed class HelpProvider 
{
	//Nome del file guida
	private const string ChmFileName = "Docs.chm";
	private Form _form;

	internal Form Form 
	{ 
		get 
		{ 
			return (this._form); 
		} 
	}

	private HelpProvider()
	{
		this._form = null;
	}

	internal HelpProvider(Form form)
	{

		HelpNamespace = Path.Combine(
			Environment.CurrentDirectory,
			ChmFileName);
		
		_form = form;
		_form.HelpRequested += new HelpEventHandler(OnHelpRequestedAction);
	}

	internal void TryAsk()
	{
		Control ctrl = null;
		if ((this._form.ActiveControl != null) 
			&& 
		    (!IsNull(this._form.ActiveControl)))
			ctrl = this._form.ActiveControl;
		else
			ctrl = _form;

		if (ctrl.Tag == null)
		{
			Program.ShowWarning("Unable to retrive help information for this argument.");
			return;
		}

		Help.ShowHelp(ctrl, HelpNamespace, HelpNavigator.TopicId, ctrl.Tag as string);
	}

	/// <summary>
	/// Event Handled when an help request is execute by pressing F1
	/// </summary>
	/// <param name="sender" />
	/// <param name="hlpevent" />
	private void OnHelpRequestedAction(object sender, HelpEventArgs hlpevent)
	{
		this.TryAsk();

		hlpevent.Handled = true;
	}
}

Il funzionamento è molto semplice, questo oggetto riceve in ingresso un Form su cui imposta l’evento  HelpRequested per poter successivamente intercettare le richieste da parte dell’utente, resta in attesa, quando una richiesta viene inoltrata, viene scatenato l’evento OnHelpRequestedAction in cui viene invocato il metodo TryAsk.

Questo metodo effettua diverse operazioni, prima di tutto legge il valore del Tag del controllo che gli viene passato, se questo valore è diverso da NULL,  invoco un metodo, ShowHelp, che fornisce la classe Help del .Net Framework in cui passo:

  • ctrl: Controllo che chiede Aiuto (un form o un qualsiasi controllo presente nella maschera corrente)
  • HelpNamespace: nome del file di guida da invocare per la documentazione
  • HelpNavigator.TopicId: modalità di lettura della documentazione,cioè, quando apro la guida, cosa devo fare esattamente, se cercare per argomento, per indice etc.
  • ctrl.Tag: identificativo dell’argomento di cui vogliamo le informazioni.

L’HelpProvider, può essere facilmente utilizzato in questo modo: nel form in cui vogliamo rendere attiva la funzionalità di help, dichiariamo una variabile privata di tipo HelpProvider che andiamo ad inizializzare nel costruttore del nostro Form come nell’esempio che segue:

public class MyWindowsApp : System.Windows.Forms.Form {
	private HelpProvider _helpProvider;
	
	public MyWindowsApp() {
		_helpProvider = new HelpProvider(this);
	}
}

Con queste poche righe di codice abbiamo attivato un help in linea completo e funzionante!

Ricordate che per funzionare è necessario che i controlli e / o direttamente i form per cui devono essere disponibili informazioni, devono avere la variabile Tag valorizzata con un certo indice equivalente ad un TopicId del nostro file “Windows Compiled HTML Help”, in caso contrario l’help non viene mostrato.

Quando un Design Pattern ti evita l’esaurimento nervoso

22 ottobre 2010

Tempo fa’ (tanto tempo), un mio amico per cui nutro grandissima stima, mi ha consigliato un libro molto interessante in cui, molti dei Design Pattern che oggi si utilizzano, vengono spiegati ed approfonditi cosi attentamente da risultare praticamente impossibile non capire cosa c’è scritto.

Sto parlando di un libro intitolato Patterns of Enterprise Application Architecture di Martin Fowler, uno dei migliori acquisti che abbia mai fatto!

Oggi, a distanza di moltissimo tempo (circa 6 mesi), ho rimesso mani su un progetto di cui, io stesso, l’autore, ne avevo dimenticato completamente l’esistenza. Un sistema abbastanza complesso per l’elaborazione di dati provenienti da diverse sorgenti, un contenitore di informazioni allo stato puro, un “Tracking System”, un fattorino, un sistema di lavaggio, in somma una miriade di funzionalità, si documentate (da un analisi allegata sempre scritta da me), ma… come fai a distanza di 6 mesi a metterci mani per modificare il codice?

Mi collego al magnifico (si fa’ per dire, prenderei volentieri a pugni colui che ha inventato questo sistema di versioning) Microsoft Visual Source Safe aziendale e scarico tutto il progetto, durante l’operazione di Download inizio già ad avere la pelle d’oca, i file sono tantissimi, ed inizio a pensare “Da dove devo iniziare?”.

Completato l’allineamento apro la Soluzione e SORPRESA!

Ritrovo un progetto che mi ricorda tanto ASP.NET MVC (peccato che lo stesso progetto è realizzato, per questioni di Legacy, in .Net Framework 2.0) per la sua struttura interna:

  • Content
  • Models
  • Views

La mia prima reazione? Un Sorriso, immaginavo di aver messo insieme diverse tecniche e utilizzato molteplici strumenti per realizzare qualcosa di molto molto facile (data la futura previsione di introdurre miei colleghi allo sviluppo dello stesso). Provo ad espandere la voce Models e capisco subito con cosa ho a che fare guardando semplicemente la ripetizione dei suffissi con cui i nomi file si alternano in questa cartella:

  • Entity1.cs
  • Entity1TableGateway.cs
  • Entity2.cs
  • Entity2TableGateway.cs
  • Build.bat

Figo! Non ricordavo di aver usato un Design Pattern (sarebbe stato anormale il contrario in un progetto del genere), la cosa che non capisco è quel Build.bat finale, la cosa simpatica è il commento che 6 mesi prima ci ho lasciato dentro!

/*
 *	Ciao Roberto! Sono sempre io... Roberto, cioè sono tu!
 *	Immaginavo non ti ricordassi di me, ti scrivo, in 
 *	questo commento per darti una fantastica notizia, 
 *	ti basta eseguire questo batch, se hai modificato 
 *	la struttura delle tablle nel db, per rigenerare
 *	l'intera base dati sotto .Net con un misto Design 
 *	Pattern (TableGateway e Transaction Script)
 *	
 *	Tuo, tu.
 */

Si, mi sono commosso davvero tanto leggendo queste parole…. ho riso come un pazzo per 5 minuti! La bellezza dei Design Pattern la si nota per due motivi:

  1. Te ne ricordi facilmente il funzionamento.
  2. Quando hai capito come funziona la giostra, sei in grado di creare la catena di montaggio che la costruisce!

ASP.NET – Trasformazione del Web.Config (Debug | Release)

24 settembre 2010

Chi sviluppa applicazioni web ASP.NET avrà famigliarità con la gestione del web.config, questo file generalmente contiene tutte le impostazioni della nostra applicazione web, dalle stringhe di connessione ai messaggi di errore passando per chiavi personalizzate (appSettings), configurazione di account email, Authentication Provider e tantissimi altri servizi che è possibile configurare direttamente qui.

Ovviamente la configurazione delle nostre applicazioni cambia notevolmente quando passiamo, per esempio, da un ambiente di test a quello di esercizio, sicuramente avremo a che fare con modiche di diverso tipo, personalmente nella maggior parte dei casi mi trovo a dover apportare qualche piccolo cambiamento alle stringhe di connessione (dal server di test a quello di esercizio cambiano e non sarebbe normale il contrario!),poi utilizzando servizi esterni mi è utile modificare le API Key per l’autenticazione con gli stessi, per esempio, per quanto riguarda Google Maps è necessario essere in possesso di una API Key registrata su ogni host in cui lavoriamo (io ho registrato localhost e anche ambiente-di-deploy, ne consegue che in fase di debug utilizzerò la prima, pubblicando la Release dovrò cambiare ed utilizzare la seconda).

Con .Net Framework 4 è stata introdotta la possibilità di poter gestire il web.config in maniera molto diversa rispetto al passato, mentre prima era necessario modificare manualmente i valori di questo file di configurazione, ora è possibile manipolare direttamente da progetto le informazioni contenute, con delle regole di controllo che, in base alla modalità di compilazione (Release o Debug) , generano un diverso file di configurazione. Aprendo il progetto, per il file web.config, troviamo una struttura del genere:

  • Web.config
    • Web.Debug.config
    • Web.Release.config

È facile intuire che partendo dall’elemento principale, le due sotto voci non sono altro che le diverse configurazioni disponibili per il Deploy. Non è, come si potrebbe credere, un copia incolla della stessa configurazione root su cui apportare delle modifiche, si tratta invece, di un manipolatore di stringhe, ognuno dei due file (Debug e Release) viene predisposto in modo tale da permettere all’utente di manipolare sempre l’elemento root ma secondo precise regole.

Per l’intercettazione degli elementi da manipolare, all’interno dello stesso file, è possibile utilizzare diversi attributi, uno di questi si chiama xdt:Locator con cui è possibile specificare un pattern di ricerca per gli elementi che corrispondono alle caratteristiche indicate, la sintassi da utilizzare per le ricerca viene ereditata direttamente da XPath.

Per capire quanto detto fino ad ora, ho pensato di realizzare un esempio pratico. Supponiamo di avere il nostro web.config che contiene le seguenti informazioni:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
 <connectionStrings>
  <add 
	name="Connessione" 
	connectionString="Initial Catalog=TestDB;Integrated Security=SSPI" 
  />
 </connectionStrings>
</configuration>

Ho inserito una una stringa di connessione, questa attualmente punta al database di Test, manipolando il file Web.Release.Config posso specificare la stringa di connessione che deve essere utilizzata quando l’applicazione viene compilata in modalità Release:

<?xml version="1.0" encoding="utf-8">
<configuration>
 <connectionStrings>
  <add 
	name="Connessione" 
	connectionString="Initial Catalog=TestDB;Integrated Security=SSPI" 
        xdt:Transform="Replace"
  />
 </connectionStrings>
</configuration>

Cosa accade? Avendo impostato in modalità Release una nuova stringa di connessione, quando andrò a pubblicare la mia web-application direttamente in ambiente di deploy, facendo attenzione ad impostare la modalità di compilazione su Release, viene pubblicata una versione del web.config che contiene le informazioni della ConnectionString specificata in Web.Release.Config!

ASP.NET MVC – ViewData vs TempData

30 luglio 2010

Questo Framework mette a disposizione diverse funzionalità, una delle tante, che ritengo davvero molto utile, è la possibilità di far transitare dati tra diverse richieste http, questa possibilità si materializza attraverso l’utilizzo di diverse proprietà all’interno della pagina web: ViewData e TempData. Non nascondo la mia iniziale confusione nel capire a cosa servissero due variabili che hanno quasi lo stesso nome, ma poi tutto ha avuto un senso più chiaro e logico!

In due parole: TempData è come ViewData con una sola differenza: TempData può contenere informazioni che vengono conservate per le (massimo) due richieste successive, oltre, il suo contenuto viene automaticamente distrutto. Quindi può essere utilizzata per passare messaggi di errore e roba del genere tra diverse Action, mentre ViewData permette un interazione diretta tra Controller e View.

un mio amico ha chiarito ancor meglio il concetto: in parole povere, il tempdata permette di passare dati tra controller mentre il viewdata è adibito a quelle logiche che restano racchiuse nella stretta corrispondenza view-cotroller (yetanothergeek).

Un esempio concreto può chiarire il concetto, supponiamo di dover implementare due diversi comportamenti:

  • Un login.
  • Il caricamento di dati all’interno di DropDownList presenti in una View.

Per quanto riguarda il Login supponiamo di avere un UserController che implementa diverse view tra le quali c’è appunto Login:

public class UserController {
    ...
    public ActionResult Login(string username, string password) {
	if(!IFormProvider.Authenticate(username, password))
             TempData["LoginErrors"] = "Autenticazione fallita.";
	return RedirectToAction("Index", "Home");
    }
}

La view non è fisicamente presente ma si occupa di verificare che username e password inseriti siano validi, nel caso in cui questa condizione non si verifichi, viene caricato un valore in TempData, LoginErrors, che contiene un avviso per l’utente, successivamente viene effettuato un RedirectToAction che realmente equivale ad un Response.Redirect, se avessimo utilizzato ViewData per memorizzare questo messaggio di errore, la Redirect non ci avrebbe permesso di memorizzare l’avviso per l’utente, TempData invece mantiene lo stato dell’informazioni per la prossima richiesta e se necessario anche per la successiva! Quindi è chiaro che sarà utile per noi sfruttare questo contenitore quando sappiamo di dover spostarci da una Action ad un’altra.

Ora passiamo al secondo esempio in cui è necessario passare dal Controller, informazioni aggiuntive alla View (supponendo di avere già una View con un Model fortemente tipizzato), sempre in UserController abbiamo un’altra view che chiamiamo List, in questa, sono presenti l’elenco completo degli utenti ed un filtro di ricerca con possibilità di filtrare per Ruolo, quindi, oltre a fornire l’elenco degli utenti trovati, dobbiamo fornire un elenco di ruoli che devono essere caricati nella DropDownList:

public class UserController {
    private DatabaseDataContext db = new DatabaseDataContext();
    ...
    private void BindRequiredData() {
	// Elenco completo dei ruoli
	ViewData["Roles"] = new SelectList(db.Roles, "RoleID", "Name");
    }

    [HttpGet]
    public ActionResult List() {
        BindRequiredData();
    }

    [HttpPost]
    public ActionResult List(int RoleID) {
	BindRequiredData();

        // Prelevo gli utenti.
        var users = db.Users.Where(u => u.RoleID == RoleID);

        return View(users.ToList());
    }
}

[ad#ad-lungo]

Ho implementato ambedue le viste, GET e POST, ho preparato un metodo privato che provvede a caricare in ViewData l’elenco dei Ruoli, successivamente nella pagina web posso sfruttare questa informazioni per la generazione della DropDownList. In questo caso si nota come l’utilizzo di ViewData è più adatto al contesto, il suo ciclo di vita equivale a quello della richiesta che, una volta completata, rilascerà ogni risorsa utilizzata.

Condivido queste informazioni in quanto io per prima mi sono ritrovato ad avere falsi problemi di ViewData non funzionante, pretendevo di poter trasmettere messaggi tra diverse Action utilizzando direttamente questo contenitore, poi scoprendo TempData ed analizzando analogie e differenze, tutto è risultato molto più chiaro!