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ù!

ASP.NET Application State

3 agosto 2010

Quando usarla? Quando è più giusto scegliere questo contenitore mettendo da parte la cache? Prima di rispondere a questa domanda è necessaria una breve introduzione sull’argomento. L’Application State è un contenitore di dati accessibile da tutte le classi presenti all’interno di un’applicazione web. Diversamente dalle sessioni, la cui accessibilità e ristretta all’utente che ne è proprietario, l’Application State è accessibile a tutti gli utenti (proprio come la cache). Per tale motivo questo contenitore si presta bene per la memorizzazione di piccole quantità di dati spesso richieste all’interno dell’intero ciclo di vita dell’applicazione.

L’Application State viene fornito tramite un istanza di HttpApplicationState che espone un dizionario di key(string)/value(object). Questo oggetto viene istanziato la prima volta quando un qualsiasi utente si collega all’applicazione web ed è successivamente disponibile per tutto il ciclo di vita dell’applicazione stessa.

Generalmente l’accesso gestito tramite l’HttpContext (HttpContext.Current.Application) da cui possiamo inserire, modificare e cancellare valori. Le modalità con cui effettuare questa operazione viene effettuata necessitano particolare attenzione, a differenza della cache, in cui basta semplicemente richiamare il metodo Add o Insert per memorizzare valori, nell’Application State è necessario isolare il contenitore di dati per averne l’accesso esclusivo essendo questa una risorsa globale. Nell’esempio viene chiarito il concetto:

DataTable data = database.GetData();
HttpContext.Current.Application.Lock();
HttpContext.Current.Application.Add("Data", data);
HttpContext.Current.Application.UnLock();

[ad#ad-lungo]

Come potete notare, ho reso più visibile la porzione di codice prima e dopo l’inserimento, fondamentale se vogliamo assicurare che questo contenitore funzioni bene. Prima di utilizzare questo strumento è necessario conoscere alcune informazioni preliminari:

  • Essendo questa una risorsa incorporata permette di ottenere prestazioni ottimali (molto più veloci del salvataggio dei dati su database o file system). Se però la quantità dei dati memorizzati è molto grande, viene esaurita anche la memoria a disposizione del server web producendo più problemi che benefici, quindi se la quantità di dati da salvare è molto grande sarebbe opportuno pensare ad una soluzione alternativa come la cache.
  • I dati memorizzati in questo contenitore sono Volatili, ciò significa che se l’applicazione web viene riavviata o terminata per diverse motivazioni (modifiche al web.config, crash etc.) i dati vengono persi. Si deduce che non è una buona pratica affidarsi alla completa memorizzazione di informazioni nel contenitore Application ma è opportuno affidarsi anche alla gestione di questi dati in una memoria non volatile da cui poterli successivamente ripristinare.
  • Non è una soluzione adatta alla scalabilità: l’Application State non viene condiviso se stiamo lavorando in una web-farm o scenari simili (server multiprocessori, macchine virtuali etc.), quindi in questi casi è opportuno pensare ad un’alternativa più efficiente (come una vera e propria base dati e quindi l’utilizzo di un server di database) che possa assicurare il recupero dei dati in ogni momento.

Fatte queste premesse, penso sia possibile valutare quando e come introdurre l’Application State nelle proprie applicazioni!

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!