ASP.NET MVC – Caricare un file JAVASCRIPT EMBEDDED.

25 luglio 2012

Appunto qui per ricordarmene in futuro (visto che dimentico tutto ultimamente). Ho realizzato una libreria di classi in cui, oltre ad oggetti C#, sono presenti diversi file JAVASCRIPT il cui contenuto è necessario perché le classi possano funzionare correttamente all’interno di un’APP MVC.

Come è possibile caricare questi file all’interno di un progetto di questo tipo? Con pochi è semplici passaggi il lavoro è fatto.

  1. Selezionare il file JAVASCRIPT che è necessario importare all’interno del progetto libreria.
  2. Dal menu contestuale (click destro del mouse), selezionare la voce Proprietà.
  3. Nella finestra delle Proprietà modificare Build Action: Embedded Resource.
  4. Nel file AssemblyInfo.cs della del progetto libreria aggiungere una nuova riga di codice:
    [assembly: System.Web.UI.WebResource("YourNamespace.Resource.js, "application/x-javascript")]

Questi passaggi bastano per poter incorporare la libreria all’interno di un’applicazione ASP.NET Web Forms utilizzando ClientScript.GetWebResourceUrl(Type, resourceName), con MVC è necessario eseguire ulteriori passaggi per poter rendere tutto funzionante. Realizziamo per questo scopo un oggetto con cui deve essere possibile ottenere l’URL dei file embedded molto semplicemente.

public static class ResLocator
{
	private const string UrlLocatorMethodName = "GetWebResourceUrlInternal";

	public static string Resolve(Type assemblyObjectType, string resourceName) 
	{
		MethodInfo resourceLocatorMethod = assemblyObjectType.GetMethod(UrlLocatorMethodName, 
			BindingFlags.NonPublic | BindingFlags.Static);
		string url = string.Format("/{0}", resourceLocatorMethod.Invoke(
			null,
			new object[] { Assembly.GetAssembly(assemblyObjectType), resourcePath, false })
		);

		return url;
	}
}

Il metodo Resolve accetta in input un Type che deve necessariamente riferirsi ad un tipo di oggetto contenuto nel progetto libreria e resourceName, cioè il nome della risorsa da includere. Successivamente, referenziando il namespace in cui è contenuto l’helper all’interno della nostra MasterPage, è possibile includere lo script di riferimento in questo modo:

<script 
	type="text/javascript" 
	src="@ResLocator.Resolve(typeof(SampleAssemblyObject), "YourNamespace.Resource.js)" />

Completati questi passaggi ed avviata la nostra applicazione MVC, il risultato dell’inclusione è simile a questo:

/WebResource.axd?d=HWyLh7g77XDqhYfNG0fioE3hSIzYB4uYmoUs3cJSuFRbnk9cZT1AWVuijZ81&t=634788092174760080

Spero possa essere utile anche ad altri per eventuali EMBEDDED RESOURCES!

ASP.NET MVC & Microsoft Chart Controls

7 novembre 2011

Ultimamente mi sono concentrato molto sull’utilizzo di ASP.NET MVC per lo sviluppo di un progetto abbastanza grosso, tra le varie cose è sorta l’esigenza di poter utilizzare uno strumento per la generazione di grafici di vario tipo.

All’inizio la mia scelta è ricaduta su “Libero API for Fusion Charts”, un prodotto fatto davvero molto bene e che offre la possibilità di implementare diverse tipologie di grafico (tante!). Nonostante le sue caratteristiche ho dovuto mettere da parte questa libreria in quanto legata all’utilizzo del componente Flash (sempre questioni di legacy).

Alla fine ho optato per la soluzione che poi si è rivelata anche la migliore (e ancora mi chiedo perché non ci ho pensato prima?). La scelta è ricaduta su “Microsoft Chart Controls”.

Partiamo dal presupposto che questo prodotto non si presta bene, se non dopo qualche piccolo accorgimento, per essere utilizzato con ASP.NET MVC. Infatti nasce per funzionare principalmente all’interno di applicazioni Web Forms, la cui struttura è nettamente diversa da quella di un progetto MVC. Essendo il chart un controllo Server, necessita di una pagina web di tipo System.Web.UI.Page in cui poter essere posizionato, sappiamo bene che in una pagina MVC non è possibile inserire controlli di questo tipo.

Oggi vorrei spiegarvi come configurare il vostro progetto MVC perché i Microsoft Charts possano funzionare correttamente e senza alcun tipo di problema.

Prima di tutto è necessario estendere le funzionalità dell’oggetto Page perché possa occuparsi anche del disegno dei grafici ms. Lo facciamo realizzando un helper come nell’esempio che segue:

namespace MvcApplication.Helpers
{
	public static class HelperCharts
	{
		public static void RenderChart(this Page page, System.Web.UI.DataVisualization.Charting.Chart chart)
		{
			var writer = new HtmlTextWriter(page.Response.Output);
			chart.RenderControl(writer);
		}
	}
}

Chiaramente le istruzioni che abbiamo scritto sono poche e semplici, ritengo in ogni caso che lavorare in questo modo garantisce una migliore leggibilità e pulizia del codice  e, soprattutto per quanto riguarda MVC, rispetto delle regole imposte dal pattern.

Completato il metodo dobbiamo assicurarci che tale funzionalità sia poi disponibile in tutte le Views e che la gestione dei Charts possa essere correttamente eseguita dal .Net Framework, per tale motivo è necessario il file web.config:

<configuration>
	<appSettings>
		<add key="ChartImageHandler" value="storage=file;timeout=20" />
	</appSettings>
	<system.web>
		<httpHandlers>
		<add path="ChartImg.axd" verb="GET,HEAD,POST" 
			type="System.Web.UI.DataVisualization.Charting.ChartHttpHandler, 
				System.Web.DataVisualization, Version=4.0.0.0, 
				Culture=neutral, PublicKeyToken=31bf3856ad364e35"
	        	validate="false" />
		</httpHandlers>
		<pages>
			<namespaces>
				<add namespace="MvcApplication.Helpers" />
			</namespaces>
		</pages>
	</system.web>
</configuration>

Come da documentazione è necessario aggiungere la chiave ChartImageHandler per definire le modalità di generazione del grafico (considerando che questo alla fine non è altro che un’immagine). Nell’elenco degli HttpHandlers è necessario configurare la risorsa utilizzata per la generazione dei grafici e, per concludere, volendo rendere visibile la funzione precedentemente scritta (RenderChart) in tutte le viste senza dover effettuare una import in ogni pagina del namespace, aggiungiamo all’elenco dei default namespaces anche quello in cui risiede il metodo da noi implementato.

In seguito possiamo realizzare un DTO fatto in questo modo:

namespace MvcApplication.Models.DTO
{
	public sealed class SampleDTO
	{
		public Chart Chart { get; private set; }

		public SampleDTO(Chart chart)
		{
			Chart = chart;
		}
	}
}

Possiamo ora creare un controller di esempio che dispone di un’Action che restituisce il DTO precedentemente realizzato:

namespace MvcApplication.Controllers
{
	using MvcApplication.Models.DTO;
	public class SampleController : Controller
	{
		[HttpGet] public ActionResult GetSampleChart()
		{
			Chart chart = new Chart();
			// Configure Chart...
			SampleDTO sampleDTO = new SampleDTO(chart);
			return View(sampleDTO);		
		}
	}
}

E per concludere si passa alla View che si presenta come nell’esempio:

<%@ Page 
	Title="Sample" 
	Language="C#" 
	MasterPageFile="~/Views/Shared/Shell.Master" 
	Inherits="System.Web.Mvc.ViewPage<MvcApplication.Models.DTO.SampleDTO>" %>
<h2>Chart Sample</h2>
<div>
<% 	Page.RenderChart(Model.Chart); >
</div>

Con poche righe, abbiamo realizzato quanto necessario per poter sfruttare in modo corretto e pulito i Microsoft Charts nel nostro progetto MVC.

Enjoy!

IIS – “Could not load file or assembly ‘Oracle.DataAccess’ or one of its dependencies. An attempt was made to load a program with an incorrect format.”

27 giugno 2011

In quest’ultimo periodo mi sono occupato dello sviluppo di un progetto basato su ASP.NET MVC ed Oracle. Per l’accesso ai dati abbiamo utilizzato Oracle.DataAccess, una libreria presente all’interno di un pacchetto di strumenti raggruppati sotto il nome di “Oracle Client”.

Nel dover rilasciare l’applicativo si è verificato un particolare problema all’interno del Web Server IIS ospitante. Provando a chiamare l’applicazione da Browser veniva visualizzato il seguente messaggio di errore:

Could not load file or assembly 'Oracle.DataAccess' or one of its dependencies. An attempt was made to load a program with an incorrect format.

Provando a cercare online una possibile soluzione al problema mi sono imbattuto in una serie di Post infiniti in cui vengono proposti particolari workground da applicare direttamente sul file web.config per risolvere un problema di riferimenti che si creano durante l’utilizzo della libreria. C’è da notare che questo problema non si verifica se si lavora con il server web Cassini integrato in Visual Studio.

Poi però ho trovato una soluzione meno invasiva a livello applicativo ma configurabile direttamente sul web server. In pratica per risolvere il problema è necessario apportare una piccola modifica al Pool Applicativo utilizzato per l’esecuzione dell’applicazione quando questa gira su un server a 64bit. Aprendo Internet Information Services Manager è possibile, all’interno della voce “Application Pools” configurare in maniera più dettagliata uno di questi pool selezionando la voce “Advanced Settings…” subito dopo averne selezionato uno.

Per l’applicazione MVC realizzata è stato scelto come pool predefinito “ASP.NET v4.0”, nelle opzioni avanzate ho spuntato la voce “Enable 32-Bit Applications” in modo tale da permettere l’esecuzione di codice a 32 ed a 64 bit insieme.

Rigenerando il pool e riavviando l’applicazione, il problema non si presenta più!

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.