Gestire le risorse con SparkViewEngine
Già nel post precedente ho introdotto SparkViewEngine; in questo voglio mostrare un’interessante feature che apre diversi scenari di mantenibilità e di “servizio”, come nel caso di Dexter che mostrerò più avanti.
Sicuramente ci sarà capitato molto spesso di dover “hostare” le nostre applicazioni all’interno di una virtual directory, e magari di doverle spostare successivamente sulla root di un sito e viceversa: spesso questo risulta scomodo in quanto può comportare alcune modifiche ai percorsi dei file di risorse (immagini, css, javascript, etc). Se proviamo a guardare il lato pratico, un codice html simile a questo:
<link type="text/css" rel="stylesheet" href="/Styles/Site.css" />
non sarebbe più valido nel caso il sito fosse spostato all’interno di una virtual directory, e dovrebbe diventare una cosa del tipo:
<link type="text/css" rel="stylesheet" href="/MyVirtualDirectory/Styles/Site.css" />
Ovviamente il problema è risolvibile sfruttando un helper che effettua il resolve dell’url, con il conseguente svantaggio di aggiungere codice all’interno del markup, rendendo difficile un eventuale refactoring; a questo si aggiunge la perdita in leggibilità del codice html. Un’altra soluzione è far gestire i percorsi delle risorse a Spark (devo dire che lo fa egregiamente e con estrema semplicità) : in primis è necessario modificare il web.config aggiungendo la sezione di Spark, come mostrato di seguito:
<section name="spark" type="Spark.Configuration.SparkSectionHandler, Spark" requirePermission="false"/>
</configSections>
<spark>
<compilation debug="false"/>
<pages automaticEncoding="true">
<namespaces>
<add namespace="System" />
<add namespace="System.Web" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Linq" />
</namespaces>
<resources>
<add match="~/Scripts" location="/Resource/Scripts" />
<add match="~/Styles" location="/Resource/Styles" />
<add match="~/Images" location="/Resource/Images" />
<add match="~/Media" location="/Resource/Media" />
</resources>
</pages>
</spark>
Come potete intuire la sezione resource è quella più interessante, e ci permette di specificare dove sono presenti i files delle risorse: quindi, lato view, è sufficiente utilizzare il tilde per indicare il percorso iniziale dell’applicativo, poi ci penserà spark in fase di rendering a sostituirlo con quanto specifica nel file di configurazione.
Se proviamo a renderizzare questo codice html con i settaggi sopra specificati, il rendering finale dovrebbe essere questo:
<link type="text/css" rel="stylesheet" href="~/Styles/Site.css" /> <link type="text/css" rel="stylesheet" href="/Resouce/Styles/Site.css" />
Ovviamente questo può aprire un ulteriore scenario, ossia offrire la possibilità a chi sviluppa la parte html di utilizzare alcune CDN (google, Microsoft, etc) senza doverne conoscere il percorso; di fatto, chi vuole sviluppare una skin per dexter e vuole utilizzare la cdn di Microsoft per jQuery, può semplicemente scrivere questo:
<script src="~/Scripts/CDN/jQueryTools/1.2.2/jquery.tools.min.js" type="text/javascript" language="javascript"></script>
Così facendo si può cambiare in un qualsiasi momento la CDN da utilzzare, o scegliere di hostare il file con la libreria su un proprio server.
Di seguito riporto il blocco di configurazione di spark in dexter, che mostra le varie CDN supportate:
<spark>
<compilation debug="false"/>
<pages automaticEncoding="true">
<namespaces>
<add namespace="System" />
<add namespace="System.Web" />
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
<add namespace="System.Linq" />
<add namespace="Dexter.Web.Site.Models.Blog" />
<add namespace="System.Collections.Generic" />
<add namespace="Dexter.Web.Mvc.Helpers" />
<add namespace="Dexter.Core.Configuration" />
<add namespace="Dexter.Core.Concrete" />
<add namespace="Dexter.Web.Mvc.Controls" />
</namespaces>
<resources>
<add match="~/Scripts/CDN/Microsoft" location="http://ajax.microsoft.com/ajax"/> <!-- http://www.asp.net/ajaxlibrary/cdn.ashx -->
<add match="~/Scripts/CDN/Google" location="http://ajax.googleapis.com/ajax/libs"/> <!-- http://code.google.com/apis/ajaxlibs/documentation/#AjaxLibraries -->
<add match="~/Scripts/CDN/jQueryTools" location="http://cdn.jquerytools.org"/> <!-- http://flowplayer.org/tools/download/index.html -->
<add match="~/Scripts" location="~/Scripts" />
<add match="~/Styles" location="~/Styles" />
<add match="~/Images" location="~/Images" />
<add match="~/Media" location="~/Media" />
</resources>
</pages>
</spark>
Ciauz
Unblock delle assembly
Oggi mi è capitato un errore alquanto curioso che, se non si sta molto attenti, può portare via parecchio tempo; ma partiamo con ordine.
Il buon Mauro mi ha passato un set di librerie da utilizzare in un applicativo interno che avevo necessità di aggiornare, ed il trasferimento (per puri motivi di pigrizia) è avvenuto via messenger. Ovviamente, senza pensarci su troppo, ho copiato le nuove librerie all’interno della bin in modo da aggiornare l’applicazione all’ultima versione ma, dopo il primo avvio, ricevevo la seguente eccezione:
Security Exception
Description: The application attempted to perform an operation not allowed by the security policy. To grant this application the required permission please contact your system administrator or change the application's trust level in the configuration file.
Exception Details: System.Security.SecurityException: Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
Stack Trace:
[SecurityException: Request for the permission of type 'System.Web.AspNetHostingPermission, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed.]
System.Reflection.Assembly._GetType(String name, Boolean throwOnError, Boolean ignoreCase) +0
System.Reflection.Assembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase) +42
System.Web.UI.Util.GetTypeFromAssemblies(ICollection assemblies, String typeName, Boolean ignoreCase) +145
System.Web.UI.TemplateParser.GetType(String typeName, Boolean ignoreCase, Boolean throwOnError) +73
System.Web.UI.TemplateParser.ProcessInheritsAttribute(String baseTypeName, String codeFileBaseTypeName, String src, Assembly assembly) +111
System.Web.UI.TemplateParser.PostProcessMainDirectiveAttributes(IDictionary parseData) +279
Il messaggio era alquanto preoccupante e, dopo vari check della configurazione (full trust mode, etc), ho trovato la soluzione nell’unblock del file; di fatto, essendo questi arrivati da una fonte non sicura, Windows ha giustamente pensato di impedirne l’utilizzo salvo previa autorizzazione dell’utente stesso, che può essere effettuata tramite la finestra delle proprietà, come da screenshot seguente:
Errore 404.3 con un servizio WCF hostato in IIS
Posted by imperugo in Windows Communication Foundation on Thursday 01 July 2010 at 9:45 AM
Ultimamente in Dexter stiamo aggiungendo una batteria di servizi per poter realizzare delle applicazioni client che possano interagire con il nostro engine e, nello specifico, applicazioni riguardanti il neonato Windows Phone 7, IPhone, IPad ed Android.
Testando il primo servizio sono incappato subito in un errore riguardante la raggiungibilità del servizio stesso, ricevendo da IIS un HTTP Error 404.3 - Not Found, nonostante che il servizio fosse presente e correttamente configurato.
Il problema era dovuto al fatto che nel mio IIS locale non erano registrati tutti i moduli necessari al funzionamento del servizio; tutto ciò è facilmente risolvibile eseguendo dal prompt del dos I seguenti comandi:
cd c:\windows\Microsoft.Net\Framework\v3.0\Windows Communication Foundation\
ServiceModelReg –i
A questo punto nella schermata dovrebbero apparire tutta una serie di messaggi che informano l’utente su cosa è stato installato.
Scopo del post è ricordarsi il comando ![]()
Ciauz
Yatta, I dit it (071-519)
Esame Designing & Developing Web Applications using Microsoft .NET Framewrok 4.0 passato!

Peccato non aver potuto dare l’altro ![]()
SparkViewEngine Kick Off
Già nel post precedente avevo annunciato una serie di contenuti riguardanti SparkViewEngine. Per approfondire l’utilizzo di questo engine ho cominciato il porting della skin del mio blog: devo dire che, man mano che lo utilizzo, rimango colpito dalla sua produttività e potenza, a partire dalle cose più semplici fino ad arrivare a funzioni un po’ più avanzate che permettono di creare delle vere e proprie funzioni e/o ottimizzazioni.
Per chi non abbia voglia di aspettare e voglia vedere un utilizzo un po’ più “spinto” di Spark, consiglio di dare un’occhiata al codice di Dexter e, nello specifico, alla cartella Themes/Fusion, dove si trova il porting della mia skin che è abbastanza ricca di html.
Come preannuncia il titolo, questo post ha lo scopo di mostrare come utilizzare da subito per una semplicissima applicazione SparkViewEngine, quindi configurarlo e capirne un po’ la logica.
Una volta scaricato il codice da qui, basta referenziare le due Assembly che ne permettono l’utilizzo in ASP.NET MVC, Spark.dll e Spark.Web.Mvc.Dll e registrare il nuovo ViewEngine allo startup dell’applicativo; quindi nel global.asax.cs basta inserire il seguente codice:
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
SparkEngineStarter.RegisterViewEngine();
}
A questo punto l’applicazione è abile ed arruolata a sfruttare tutte le potenzialità di Spark, ma prima di scrivere un po’ di codice nella view è importante sapere che:
- La struttura delle folder contenente le View è esattamente la stessa che si avrebbe senza spark.
- Tutti I files delle view, partial view, master, etc devono avere estensione .spark;
- All’interno della cartella shared normalmente va creato un file “_global.spark” contenente tutti i vari using ed eventuali macro necessari alla costruzione della vista;
- La master page di default (anch’essa presente nella cartella shared) si chiama “Application.spark”;
A questo punto dovrebbe esser ben chiaro il fatto che abbiamo una cartella Views/Shared ed al suo interno abbiamo la nostra master page “Application.spark”; salvo forzature esplicite, tutte le viste erediteranno da questa master, e, se lo si vuole, resta comunque possibile specificare una master differente, ma lo vedremo più avanti.
Il codice seguente mostra una master page realizzata con spark:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title><use content="title">${Model.Title}</use></title>
<meta name="description" content="${Model.Description}" />
<meta name="keywords" content="${Model.KeyWords}" />
<meta name="author" content="${Model.Author}" />
<link rel="SHORTCUT ICON" href="~/images/favicon.ico" type="image/x-icon" />
<link type="text/css" rel="stylesheet" href="~/Styles/Site.css" />
<script src="~/Scripts/jquery.watermark.js" type="text/javascript" language="javascript"></script>
<script src="~/Scripts/jquery.fancybox-1.3.1.pack.js" type="text/javascript" language="javascript"></script>
</head>
<body>
<use content="MainContent"></use>
qualcosa ......
</body>
</html>
Come potete vedere non è presente nessun codeblock a inizio pagina e le risorse tipo css, img, etc. posso essere specificate con il prefisso tilde ~/ , che verrà sostituito dall’engine di spark con la root del sito o con quello che ci è più congeniale.
Per ora ci basta creare la nostra master page e specificare i placeholder tramite il tag Use, che andremo a riutilizzare nel vista in questo modo:
<content name="MainContent">
Benvenuto Spark!
</content>
Anche qui, come potete vedere, tutti i codeblock sono spariti a vantaggio della leggibilià e del numero ridotto di righe presenti all’inteno della view (fidatevi, questo non è nulla
).
alla scoperta di SparkViewEngine
Ne avevo già parlato durante una sessione alla V UgiAlt.Net Conference, poi un lettore mi ha consigliato di inserirlo in Dexter e di parlarne un po’, così ho deciso di realizzare una serie di post su SparkViewEngine. Prima di partire con dei post tecnici su come utilizzare Spark e quali vantaggi offre, è giusto fare qualche premessa spiegando cos’è e a cosa serve :).
Spark View Engine è un template engine per ASP.NET MVC (sia versione 1.0 che versione 2.0) e Castle Project MonoRail che ha lo scopo di semplificare e potenziare la stesura del markup da parte dello sviluppatore o di colui che andrà a realizzare la “skin” della propria applicazione web.
Uno degli aspetti sicuramente più interessanti consiste nella possibilità di avere “View” miste, metà spark e metà MVC classiche e quindi ci permette di adottarlo anche in progetti già esistenti.
Con i post seguenti vedremo come Spark affronta il problema della realizzazione di codice HTML dinamico e capiremo il perchè Luis De Jardin lo ha definito un DSL; per ora ci basti pensare che è possibile ridurre, in alcuni casi anche dimezzare, il numero di righe di codice HTML/C# presenti all’interno delle nostre View, e di avere delle funzioni molto spinte come la renderizzazione di una View in formato PDF.
Stay tuned!
Code Contracts in .NET 4.0
Approfittando del giorno di vacanza mi sono messo a dare uno sguardo ai CodeContracts del .NET Framework 4.0. Per chi non li conoscesse, i CodeContracts non sono altro che un insieme di classi che permettono, grazie anche a dei tools che vanno installati, di definire delle regole di PreCondizione, PostCondizione e Invarianti di Oggetto per le nostre classi. Guardando un po’ il lato pratico, possiamo brevemente riassumere che:
- Le PreCondizioni equivalgono alla validazione dei parametri di ingresso in un determinato metodo (es: l’oggetto foo non deve essere null);
- Le PostCondizioni sono molto simili alle PreCondizioni, ma si rivolgono all’oggetto in uscita dal metodo;
- Le Invarianti di Oggetto invece si rivolgono alle proprietà di un oggetto;
Un aspetto interessante è sicuramente la possibilità di specificare queste regole anche a delle interfacce, forse in maniera abbastanza “strana”, ma pensandoci su corretta. Di fatto, ci basta creare una classe che implementa l’interfaccia a cui vogliamo assegnare i nostri Contracts e decorarla con un apposito attributo; a questo punto il compilatore farà il resto, ossia inserire tutto il necessario su chi andrà ad implementare realmente l’interfaccia.
Inoltre, grazie ai tools (disponibili qui) viene generata automaticamente la documentazione in formato XML, che successivamente può essere data in pasto a SandCastle per averne un formato leggibile :).
Concludendo, devo dire che il primo approccio è sicuramente positivo e di fatto, dopo un po’ che ci “giocavo”, mi sono apparsi in mente diversi scenari in cui poter sfruttare questa nuova interessante feature e sicuramente ne nasceranno dei posts, quindi Stay tuned!
Ciauz!
Async Methods e la keyword dynamic di C# 4.0
Il bello di avere un collega MVP su C#, ma ancor prima devMaskio, è che quando hai un dubbio sul linguaggio lui sa subito risponderti e spesso con una soluzione al tuo problema.
Sorvolando la domanda ed il perchè è nata, l’idea era quella di evitare la noiosa costruzione di apposite classi di “state” per invocare metodi in asincroni.
Osservando la parte pragmatica del problema, prima della versione 4.0 del .NET Framework quello che dovevamo fare per invocare un metodo asincrono era più o meno questo:
internal class Program
{
private static void Main(string[] args)
{
AsyncCallState obj = new AsyncCallState
{
Property1 = "String" ,
Property2 = 10 ,
Property3 = new AsyncCallState2 ()
{
P1 = "SubProperty1"
}
};
ThreadPool.QueueUserWorkItem(AsyncCall, obj);
Console.ReadLine();
}
private static void AsyncCall(object obj)
{
AsyncCallState anonymous = (AsyncCallState)obj;
string p1 = anonymous.Property1;
int p2 = anonymous.Property2;
string sP1 = anonymous.Property3.P1;
}
}
internal class AsyncCallState
{
public string Property1 { get; set; }
public int Property2 { get; set; }
public AsyncCallState2 Property3 { get; set; }
}
internal class AsyncCallState2
{
public string P1 { get; set; }
}
Come potete vedere le classi AsyncCallState ed AsyncCallState2 hanno un utilizzo ridottissimo e legato soltanto a questa chiamata, infatti difficilmente ci capiterà di riutilizzare questa classe per altre firme all’interno della nostra applicazione.
Grazie alla keyword dynamic di C# 4.0 è possibile evitare la costruzione di questa classe e risparmiare un bel po’ di tempo:
internal class Program
{
private static void Main ( string[] args )
{
ThreadPool.QueueUserWorkItem ( AsyncCall , new
{
Property1 = "String" ,
Property2 = 10 ,
Property3 = new
{
P1 = "SubProperty1"
}
} );
Console.ReadLine ();
}
private static void AsyncCall ( object obj )
{
dynamic anonymous = obj;
string p1 = anonymous.Property1;
int p2 = anonymous.Property2;
dynamic p3 = anonymous.Property3;
string sP1 = p3.P1;
}
}
Ciauz
Submit via jQuery con ASP.NET MVC 2 e Validazione Client Side, un bel cocktail
Ultimamente sto facendo parecchio uso ASP.NET MVC 2, più precisamente mi sto occupando della parte di markup, jQuery ed input di dati; di fatto in un post precedente avevo spiegato come era possibile sfruttare jQuery e le DataAnnotations per validare dei dati sul client.
Quanto detto precedentemente funziona perfettamente nel caso in cui l’oggetto che effettuerà il submit della <form> sarà un input type, al contrario se si ha la necessità di invocare il submit tramite un’immagine o un link è necessario a ricorrere al javascript.
Purtroppo se si invoca il classico metodo submit() per invocare l’action della form, non viene invocata la validazione (questo a prescindere dal fatto che si usi o no jQuery per validare la form) e di conseguenza il controllo dei dati di input avverrebbe totalmente lato server.
Il problema è facilmente aggirabile e consiste nell’invocare via javascript la validzione associata alla form e, nel caso questa venga superata, si può invocare il submit() sopracitato.
Lo script seguente mostra come fare:
<div id="submitbox" class="left">
<span class="button submit">
<a href="javascript:if($('#myForm').validate().form())$('#myForm').submit();" title="Submit form">
<span>Submit form</span>
</a>
</span>
</div>
byez
.u
Un ViewEngine per la gestione dei Themes in ASP.NET MVC 2
Tempo fa avevo parlato qui di come realizzare un ViewEngine Custom per ASP.NET MVC che permettere di gestire diversi temi per la stessa applicazione, ossia offre la possibilità di cambiare la folder dove andare a “pescare” le nostre Views, Master, etc. a runtime, ma il tutto su ASP.NET MVC 1.0.
Con la nuova release di MVC è stata aggiunta una comodissima novità, ossia il supporto alle “Aree”, che non sono altro che dei “sottositi” che hanno lo scopo di semplificare la struttura dei Controllers e delle Views presenti nella struttura principale.
L’immagine seguente rende meglio l’idea di cosa siano le Aree e di come si collochino all’interno del nostro progetto:
Cambiando un po’ la struttura delle folders ho dovuto modificare mio “vecchio” ViewEngine in modo che “digerisca” questa nuova feature.
Il codice è più o meno lo stesso del ViewEngine precedente, fatta eccezione per alcune classi (come AreaHelpers, già presente nel framework ma di tipo internal e quindi inutilizzabile).
Di seguito riporto il codice:
public partial class WebFormThemeViewEngine : WebFormViewEngine
{
private static readonly string[] masterLocationFormats = new[]
{
"~/Themes/{2}/Views/{1}/{0}.master" , "~/Themes/{2}/Views/Shared/{0}.master"
};
private static readonly string[] viewLocationFormats = new[]
{
"~/Themes/{2}/Views/{1}/{0}.aspx" , "~/Themes/{2}/Views/{1}/{0}.ascx" , "~/Themes/{2}/Views/Shared/{0}.aspx" , "~/Themes/{2}/Views/Shared/{0}.ascx"
};
public WebFormThemeViewEngine ()
{
MasterLocationFormats = masterLocationFormats;
ViewLocationFormats = viewLocationFormats;
PartialViewLocationFormats = viewLocationFormats;
}
protected override bool FileExists ( ControllerContext controllerContext , string virtualPath )
{
try
{
return File.Exists ( controllerContext.HttpContext.Server.MapPath ( virtualPath ) );
}
catch ( HttpException exception )
{
if ( exception.GetHttpCode () != 404 )
throw;
return false;
}
catch
{
return false;
}
}
/// <summary>
/// Finds the view.
/// </summary>
/// <param name = "controllerContext">The controller context.</param>
/// <param name = "viewName">Name of the view.</param>
/// <param name = "masterName">Name of the master.</param>
/// <param name = "useCache">if set to <c>true</c> [use cache].</param>
/// <returns>The page view.</returns>
public override ViewEngineResult FindView ( ControllerContext controllerContext , string viewName , string masterName , bool useCache )
{
string[] strArray;
string[] strArray2;
if ( controllerContext == null )
throw new ArgumentNullException ( "controllerContext" );
if ( string.IsNullOrEmpty ( viewName ) )
throw new ArgumentException ( "viewName must be specified." , "viewName" );
string themeName = DexterEnvironment.Instance.Context.CurrentTheme();
string requiredString = controllerContext.RouteData.GetRequiredString ( "controller" );
string viewPath = this.GetPath(controllerContext, this.ViewLocationFormats, this.AreaViewLocationFormats , viewName, requiredString, "View", useCache, out strArray, themeName);
string masterPath = this.GetPath(controllerContext, this.MasterLocationFormats, this.AreaMasterLocationFormats , masterName, requiredString, "Master", useCache, out strArray2, themeName);
if ( !string.IsNullOrEmpty ( viewPath ) && ( !string.IsNullOrEmpty ( masterPath ) || string.IsNullOrEmpty ( masterName ) ) )
return new ViewEngineResult ( CreateView ( controllerContext , viewPath , masterPath ) , this );
ViewEngineResult view = new ViewEngineResult ( strArray.Union ( strArray2 ) );
if (view.View == null)
throw new HttpException(404, "File Not Found");
return view;
}
/// <summary>
/// Finds the partial view.
/// </summary>
/// <param name = "controllerContext">The controller context.</param>
/// <param name = "partialViewName">Partial name of the view.</param>
/// <param name = "useCache">if set to <c>true</c> [use cache].</param>
/// <returns>The partial view.</returns>
public override ViewEngineResult FindPartialView ( ControllerContext controllerContext , string partialViewName , bool useCache )
{
string[] strArray;
if ( controllerContext == null )
throw new ArgumentNullException ( "controllerContext" );
if ( string.IsNullOrEmpty ( partialViewName ) )
throw new ArgumentException ( "partialViewName must be specified." , "partialViewName" );
string themeName = DexterEnvironment.Instance.Context.CurrentTheme();
string requiredString = controllerContext.RouteData.GetRequiredString ( "controller" );
string partialViewPath = this.GetPath(controllerContext, this.PartialViewLocationFormats, this.AreaPartialViewLocationFormats , partialViewName, requiredString, "Partial", useCache, out strArray, themeName);
if ( string.IsNullOrEmpty ( partialViewPath ) )
return new ViewEngineResult ( strArray );
return new ViewEngineResult ( CreatePartialView ( controllerContext , partialViewPath ) , this );
}
private string GetPath( ControllerContext controllerContext , string[] locations , string[] areaLocations , string name , string controllerName , string cacheKeyPrefix , bool useCache , out string[] searchedLocations , string themeName )
{
searchedLocations = new string[0];
if (string.IsNullOrEmpty(name))
return string.Empty;
string areaName = AreaHelper.GetAreaName(controllerContext.RouteData);
bool flag = !string.IsNullOrEmpty(areaName);
List<ViewLocation> viewLocations = GetViewLocations(locations, flag ? areaLocations : null);
if (viewLocations.Count == 0)
throw new InvalidOperationException("locations must not be null or emtpy.");
bool flag2 = IsSpecificPath(name);
string key = this.CreateCacheKey(cacheKeyPrefix, name, flag2 ? string.Empty : controllerName, areaName,themeName);
if (useCache)
{
string viewLocation = ViewLocationCache.GetViewLocation(controllerContext.HttpContext, key);
if (viewLocation != null)
return viewLocation;
}
return !flag2
? this.GetPathFromGeneralName ( controllerContext , viewLocations , name , controllerName , areaName , key , ref searchedLocations, themeName )
: this.GetPathFromSpecificName(controllerContext, name, key, ref searchedLocations);
}
private static bool IsSpecificPath(string name)
{
char ch = name[0];
if (ch != '~')
{
return (ch == '/');
}
return true;
}
private string CreateCacheKey(string prefix, string name, string controllerName, string areaName,string themeName)
{
return string.Format(CultureInfo.InvariantCulture, ":ViewCacheEntry:{0}:{1}:{2}:{3}:{4}:{5}:", new object[] { GetType().AssemblyQualifiedName, prefix, name, controllerName, areaName ?? "nullArea", themeName });
}
private static List<ViewLocation> GetViewLocations(string[] viewLocationFormats, string[] areaViewLocationFormats)
{
List<ViewLocation> list = new List<ViewLocation>();
if ( areaViewLocationFormats != null )
list.AddRange ( areaViewLocationFormats.Select ( str => new AreaAwareViewLocation ( str ) ).Cast<ViewLocation> () );
if ( viewLocationFormats != null )
list.AddRange ( viewLocationFormats.Select ( str2 => new ViewLocation ( str2 ) ) );
return list;
}
private string GetPathFromGeneralName(ControllerContext controllerContext, List<ViewLocation> locations, string name, string controllerName, string areaName, string cacheKey, ref string[] searchedLocations, string themeName)
{
string virtualPath = string.Empty;
searchedLocations = new string[locations.Count];
for (int i = 0; i < locations.Count; i++)
{
string str2 = locations[i].Format(name, controllerName, areaName,themeName);
if (this.FileExists(controllerContext, str2))
{
searchedLocations = new string[0];
virtualPath = str2;
this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
searchedLocations[i] = str2;
}
return virtualPath;
}
private string GetPathFromSpecificName(ControllerContext controllerContext, string name, string cacheKey, ref string[] searchedLocations)
{
string virtualPath = name;
if (!this.FileExists(controllerContext, name))
{
virtualPath = string.Empty;
searchedLocations = new[] { name };
}
this.ViewLocationCache.InsertViewLocation(controllerContext.HttpContext, cacheKey, virtualPath);
return virtualPath;
}
}
//Classe introdotta per il supporto alle aree
internal class ViewLocation
{
protected readonly string VirtualPathFormatString;
public ViewLocation ( string virtualPathFormatString )
{
this.VirtualPathFormatString = virtualPathFormatString;
}
public virtual string Format ( string viewName , string controllerName , string areaName , string themeName )
{
return string.Format ( CultureInfo.InvariantCulture , this.VirtualPathFormatString , new object[]
{
viewName , controllerName,themeName
} );
}
}
//Classe introdotta per il supporto alle aree
internal static class AreaHelper
{
public static string GetAreaName ( RouteData routeData )
{
object obj2;
if ( routeData.DataTokens.TryGetValue ( "area" , out obj2 ) )
{
return ( obj2 as string );
}
return GetAreaName ( routeData.Route );
}
public static string GetAreaName ( RouteBase route )
{
IRouteWithArea area = route as IRouteWithArea;
if ( area != null )
{
return area.Area;
}
Route route2 = route as Route;
if ( ( route2 != null ) && ( route2.DataTokens != null ) )
{
return ( route2.DataTokens [ "area" ] as string );
}
return null;
}
}
//Classe introdotta per il supporto alle aree
internal class AreaAwareViewLocation : ViewLocation
{
public AreaAwareViewLocation(string virtualPathFormatString)
: base(virtualPathFormatString)
{
}
public override string Format( string viewName , string controllerName , string areaName , string themeName )
{
return string.Format(CultureInfo.InvariantCulture, VirtualPathFormatString, new object[] { viewName, controllerName, areaName });
}
}
Ciauz
.u
Recent Comments