asp.net comments edit

Oggi mi è capitato di dover gestire una nuova problematica in un’applicazione ASP.NET MVC. Nello specifico mi trovavo nella condizione di avere due controller con lo stesso nome; il primo era all’interno della struttura principale dell’applicazione, il secondo dentro un Area di MVC.

Il problema è identificabile e risolvibile a livello di routing; di fatto se si prova a guardare i seguenti controller (dai namespace è facilmente identificabile l’area):

namespace Dexter.Web.UI.Areas.Admin.Controllers {
  public class HomeController : BackOfficeControllerBase {
    [AcceptVerbs ( HttpVerbs.Get )]
    [OutputCache ( VaryByParam = "id" , Duration = 600 )]
    public ActionResult Index ( string id ){
      return View();
    }
  }
}


namespace Dexter.Web.UI.Controllers {
  public class HomeController : BackOfficeControllerBase {
    [AcceptVerbs ( HttpVerbs.Get )]
    [OutputCache ( VaryByParam = "id" , Duration = 600 )]
    public ActionResult Index ( string id ){
      return View();
    }
  }
}

per la seguente Route:

routes.MapRoute (
    "Default" ,
    "{controller}/{action}/{id}" ,
    new {controller = "Home" , action = "Index" , id = UrlParameter.Optional}
);

ed ad associarla all’url http://www.miosito.com/Home/Index, si può capire come MVC non sia in grado di identificare correttamente quale dei due controller Home deve essere invocato per tale richiesta, e si trova “obbligato” a sollevare un’eccezione come la seguente:

Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.

The request for 'Home' has found the following matching controllers:
Dexter.Web.UI.Controller.HomeController
Dexter.Web.UI.Areas.Admin.Controllers.HomeController

Nulla di allarmante, il problema è facilmente risolvibile specificando il namespace contenente il Controller corretto nella registrazione della Route, come mostrato di seguito:

routes.MapRoute (
    "Default" ,
    "{controller}/{action}/{id}" ,
    new {controller = "Home" , action = "Index" , id = UrlParameter.Optional} ,
    new string[] {"Dexter.Web.UI.Controller"}
);

A questo punto l’engine di MVC sa quale controller invocare e può esaudire correttamente la richiesta web.

.net comments edit

In quest’ultimo periodo sto lavorando parecchio su Dexter e sto migrando parte del codice all’ultima release di ASP.NET MVC. Tra le varie branch, merge, update, etc, mi è capitato che il progetto web non fosse più “riconosciuto” da Visual Studio come un progetto MVC, con la scomoda conseguenza che i menù contestuali non mi offrivano più le funzioni di AddView, AddArea, etc.

Nulla di grave, ma avere la possibilità di aggiungere una View direttamente dal controller nell’esatta cartella (il tutto con un solo click) è piuttosto comodo.
Ovviamente il problema era sicuramente nel file .csproj, si trattava solo di capire cosa mancava per riattivare gli “aiuti” di Visual Studio per MVC.
Per comprendere cosa andare a toccare all’interno del file di progetto, ne ho creato uno nuovo da VS e sono andato a correggere a mano il file .csproj

A questo punto è entrato in gioco WinMerge, con cui sono andato a confrontare i due file e mi sono accorto che un nodo dell’xml è diverso tra i due. Nello specifico si trattava del nodo <ProjectTypeGuids> che contiene una serie di guid separati dalla virgola che hanno lo scopo di descrivere a Visual Studio la tipologia, il liguanggio e l’output del progetto.
Trattandosi la mia applicazione di un WAP (Web Application Project) fatto con ASP.NET MVC 3 e scritto in C#, il contenuto di questo tag deve essere così:

<ProjectTypeGuids>{E53F8FEA-EAE0-44A6-8774-FFD645390401};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Per sapere il significato di ogni GUID riporto qui una tabella che ho creato dopo un po’ di ricerche in rete:

Windows (C#) {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
Windows (VB.NET) {F184B08F-C81C-45F6-A57F-5ABD9991F28F}
Windows (Visual C++) {8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}
Web Application {349C5851-65DF-11DA-9384-00065B846F21}
Web Site {E24C65DC-7377-472B-9ABA-BC803B73C61A}
ASP.NET MVC 3 {E53F8FEA-EAE0-44A6-8774-FFD645390401}
WCF {3D9AD99F-2412-4246-B90B-4EAA41C64699}
WPF {60DC8134-EBA5-43B8-BCC9-BB4BC16C2548}
XNA (Windows) {6D335F3A-9D43-41b4-9D22-F6F17C4BE596}
XNA (XBox) {2DF5C3F4-5A5F-47a9-8E94-23B4456F55E2}
XNA (Zune) {D399B71A-8929-442a-A9AC-8BEC78BB2433}
Silverlight {A1591282-1198-4647-A2B1-27E5FF5F6F3B}

Come potete vedere la parte riguardante MVC è {E53F8FEA-EAE0-44A6-8774-FFD645390401}: una volta reinserito questo GUID all’interno del mio file .csproj tutto è tornato a funzionare per il meglio.

Ciauz

.net comments edit

Ultimamente sto lavorando parecchio con il cloud computing, ed ho avuto modo di parlarne nello specifico sia ai TechDays/WPC che in alcuni progetti interni all’azienda.

Non voglio star qui a scrivere di cosa è il cloud e quando vada scelto (magari lo farò in un post futuro), piuttosto vorrei parlare di un piccolo tip riguardante i tools di sviluppo per Windows Azure.
Di fatto, se si vuole sviluppare per questa piattaforma, è necessario scaricare (da qui) ed installare sulla propria macchina di sviluppo dei tools che permettono di “simulare” in locale l’environment che si avrà in produzione.
Senza andare troppo in profondità, ci basta sapere che i tools di Azure “wrappano” tutte le chiamate verso il client all’interno di un database Sql Server (il che è molto importante, in quanto ci permette di effettuare unit test sulle chiamate con estrema semplicità) che deve essere installato sulla macchina locale; di fatto, se non avete installato Sql sulla macchina e provate a lanciare un qualsiasi worker role o web role, riceverete dal Framework una bellissima eccezione tipo la seguente:

“Windows Azure Tools: Failed to initialize the Development Storage service. Unable to start Development Storage. Failed to start Development Storage: the SQL Server instance ‘localhost\SQLExpress’ could not be found. Please configure the SQL Server instance for Development Storage using the ‘DSInit’ utility in the Windows Azure SDK.”

Questo accade perchè l’environment locale di Azure cerca di accedere all’istanza di default di SQLExpress che, come nel mio caso, potrebbe non essere installata sulla macchina.

Per chi, come me, ha l’esigenza di sviluppare su Windows Azure Platform e non vuole installare SQL Express sulla propria macchina perchè ha già installato una versione differente di SQL (2008 R2 Developer Edition nel mio caso), può tranquillamente farlo eseguendo con privilegi amministrativi “Windows Azure SDK Command Prompt” (lo trovate all’interno del menu programmi), digitando il seguente comando:

DSInit /sqlInstance:. /forceCreate

Ovviamente se si ha un nome d’istanza differente si può sostituire il “.”, che equivale a localhost, con il proprio nome, impostato durante l’installazione di SQL Server.
A questo punto verrà creato un database con tutto il necessario per poter utilizzare i tools di Azure sulla propria macchina, senza dover passare per SQL Express.

Azure rulez!

orm comments edit

Dopo mesi di assenza, finalmente trovo di nuovo il tempo di “bloggare”, e la mia intenzione, questa volta, è di parlare di NHibernate (fresco di GA), di cui sto facendo un uso abbastanza “spinto” in un progetto.
Nello specifico ho avuto l’esigenza di dover gestire con NHibernate la creazione del database - e fin qui nulla di speciale - ma con l’obbligo di creare anche delle funzioni SQL per i vari databases supportati.

Uno dei principali requirements dell’applicazione è il supporto a ben tre versioni differenti di Sql Server più due di Oracle; per come sono strutturati il dominio ed il database, per poter effettuare determinate queries ho dovuto far uso di alcune funzioni lato database, in quanto non riproducibili tramite Object Query Language.

Fortunatamente NHibernate permette di utilizzare delle funzioni SQL Custom all’interno delle proprie queries sia se si fa uso di HQL, sia di Criteria API che di Linq.
Il loro utilizzo è veramente semplice; per prima cosa è necessario creare un proprio dialect, che erediti da quello più adatto al nostro database, e registrare le funzioni all’interno del suo costruttore, come mostrato dal codice seguente:

internal class SqlServer2008Dialect : MsSql2008Dialect {
    
    /// <summary>
    /// Initializes a new instance of the <see cref="SqlServer2008Dialect"/> class.
    /// </summary>
    public SqlServer2008Dialect ( ) {

        string monthFunction = string.Format("{0}.IsMonth", NHConfiguration.Instance.DatabaseSchema);
        string yearFunction = string.Format("{0}.IsYear", NHConfiguration.Instance.DatabaseSchema);

        base.RegisterFunction ( "IsMonth" , new StandardSQLFunction ( monthFunction , NHibernateUtil.Int32 ) );
        base.RegisterFunction ( "IsYear" , new StandardSQLFunction ( yearFunction , NHibernateUtil.Int32 ) );
    }
}

A questo punto possiamo utilizzare la funzione all’interno delle nostre query in maniera molto semplice:

Session.CreateQuery ( "from Article p where IsMonth( p.PublishDate ) = :month and IsYear( p.PublishDate ) = :year" )
    .SetParameter("month",month)
    .SetParameter("year", year)
    .SetFirstResult(pageIndex * pageSize)
    .SetFirstResult(pageIndex * pageSize)
    .SetMaxResults(pageSize)
    .SetReadOnly(!enableTracking)
    .List<Article>();

Come già detto in apertura, una delle caratteristiche dell’applicazione è la creazione ed aggiornamento del database tramite Nhibernate, il che si traduce nell’aggiungere gli scripts di creazione delle funzioni SQL lato codice; tuttavia, essendo lo schema del database impostato lato configurazione, è necessario manipolare gli scripts prima che questi siano “dati in pasto” a NHibernate per la creazione dello schema.

Gli steps da seguire sono pochi e piuttosto semplici, a dimostrazione dell’ottima struttura e flessibilità offerta da NHibernate. Per prima cosa è necessario preparare gli scripts di creazione e cancellazione delle funzioni SQL, tipo la seguente:

--SCRIPT DI CREAZIONE

CREATE FUNCTION [IsMonth]
       (
@date datetime
       )
       RETURNS int

       WITH EXECUTE AS CALLER
AS
BEGIN
  IF @date IS NULL RETURN 0
    RETURN Datepart(mm,@date)
END

GO

CREATE FUNCTION [IsYear]
(
  @date datetime
)
RETURNS int

WITH EXECUTE AS CALLER
  AS
  BEGIN
    IF @date IS NULL RETURN 0
      RETURN Datepart(yy,@date)
  END

GO

--SCRIPT DI CANCELLAZIONE

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[IsMonth]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
    DROP FUNCTION [IsMonth]
GO

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[IsYear]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT'))
    DROP FUNCTION [IsYear]
GO

È importante rimuovere gli schema del database tipo “dbo” dallo script, in modo da poter aggiungere lo schema desiderato a runtime.

A questo punto va preparato un file XML di mapping che conterrà questi scripts, come mostrato di seguito:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
    <database-object>
        <create>
      <!-- Inserire qui gli script di creazione -->
        </create>
        <drop>
      <!-- Inserire qui gli script di cancellazione -->
        </drop>
    </database-object>
</hibernate-mapping>

Da qui in poi è sufficiente eseguire una Regular Expression per poter aggiungere lo schema proveniente dal nostro file di configurazione e, solo a questo punto, è possibile passare a NHibernate il codice di XML contenente gli scripts di creazione e cancellazione delle funzioni.

db.Dialect<SqlServer2008Dialect>();
db.Driver<SqlClientDriver>();
databaseObjects = Resources.DatabaseObjects.MsSQL2008;

if (databaseObjects != null)
    databaseObjects = Regex.Replace(databaseObjects, @"(\[.*\])", string.Format("[{0}].$1", dbSchema));

if (!string.IsNullOrEmpty(databaseObjects)){
    //configuration è la configuration di NHibernate
    configuration.AddXmlString ( databaseObjects);
}

Anche in scenari in cui non si abbia l’esigenza di dover supportare diverse tipologie di database, consiglio sempre, quando possibile, di lasciare l’onere della creazionde del database all’ORM, in modo da poter creare e cancellare lo schema del database durante l’esecuzione dei nostri integration tests con molta semplicitià.

Ciauz

web dev comments edit

Attendee02In estremo ritardo (causa “troppissimo” lavoro) volevo postare alcune info su un mio prossimo speech. Infatti tra poco meno di due settimane ci saranno i TechDays, ed io avrò la fortuna di partecipare come speaker.
Per chi non lo sapesse, TechDays-WPC è sicuramente tra le più importanti conferenze tecniche disponibili in Italia; è una conferenza organizzata da Microsoft e OverNet Education, e si terrà nei giorni 23, 24 e 25 novembre qui a Milano.

Agenda, prezzi, orario ed informazioni sono disponibili sul sito ufficiale dell’evento. Per chi verrà a vedermi parlerò di Windows Azure e delle problematiche riguardanti la migrazione da On Premise a On The Cloud.

Vi aspetto!

eventi comments edit

Da ieri è disponibile il download della demo utilizzata durante lo streaming su IE9 e HTML5 allo SMAU.
Colgo l’occasione per “pubblicizzare” il progetto che ho recentemente aperto su codeplex, dove potete trovare tutte le mie demo e gli esempi che da oggi in poi utilizzerò per i vari posts.

Buon Download.

P.S.: per la demo dello storage, se fatta con IE9, richiede di “hostare” i files tramite un web server (IIS/Cassini per il mondo Microsoft) altrimenti non funziona J

asp.net comments edit

Giovedì 21 ottobre avrò l’onore ed il piacere di partecipare come speaker per Microsoft Italia allo streaming sulla presentazione di IE9.
La mia sessione sarà basata sulle novità (lato sviluppo) dell’html5, quindi canvas, semantics tags, storage, video, etc.
Ovviamente non potete perderlo! Maggiori informazioni sullo streaming e registrazione sono disponibili qui. Chi invece partecipa allo SMAU può venire direttamente a trovarci Smile.

17-10-2010 21-24-34

Ciauz

asp.net comments edit

Chi come me usa Twitter sicuramente ieri avrà notato un alternarsi di “retweet” riguardanti alcune novità di Microsoft; nello specifico è stata rilasciata la prima beta di ASP.NET MVC 3, la beta 2 di WebMatrix, ed è stato presentato NuPack, il tutto a completare un importante annuncio avvenuto nei giorni precedenti riguardante alcuni plugin per jQuery realizzati da Microsoft stessa.
Devo dire che la presentazione di NuPack mi ha lasciato un po’ spiazzato; sinceramente non me l’aspettavo, ma personalmente ritengo che l’idea sia grandiosa.

NuPack:

NuPack è un add-on per Visual Studio 2010 che facilita l’utente nell’integrazione delle principali librerie OpenSource, all’interno dei propri progetti. Per intenderci, grazie a pochi click possiamo aggiungere NHibernate, Castle, Log4Net, etc alle nostre applicazioni.
Di seguito un video dimostrativo del suo utilizzo:

L’unico neo al momento è che le librerie presenti non sono molto aggiornate, ma non credo che passerà molto tempo prima che questo avverrà.


ASP.NET MVC 3 Beta:

Come sempre nessuno stravolgimento, ma tante piccole novità che lo vanno a completare sempre di più:

  • Migliorato il supporto a Razor, mancano ancora l’intellisense ed alcune integrazioni con Visual Studio, (come la voce “vai al controller” dal menu contestuale), ma alla creazione del progetto è possibile scegliere il ViewEngine da utilizzare;
  • Nuovi Html Helpers (strongly type ovviamente) , come Chart, Crypto, WebGrid, WebImage e WebMail;
  • Dependency Injection migliorata rispetto alla preview;
  • Unobtrusive JavaScript and HTML 5: ora Ajax e la validazione fanno utilizzo dell’approccio unobtrusive JavaScript di default;
  • Intregrazione con NuPack, come MVC Contrib, jQuery, etc;

 

WebMatrix Beta 2:

Minor numero di nuove features/improvements per questa release rispetto a MVC, ma in ogni caso parliamo di novità di un certo rilievo:

  • La creazione delle pagine ora gode del supporto di Razor al pari di MVC3: di fatto ora è possibile creare pagine sia con C# che con VB, sfruttando a pieno il nuovo ViewEngine di casa MS;
  • Nuovi template che comprendono anche funzionalità riguardanti HTML5 e CSS3;
  • Una serie di toolkit installabili tramite il NuPack, che comprendono Analytics, Facebook, GamerCard, Gravatar, LinkShare, Captcha, Twitter, etc;

Ed ora la parte più interessante, i downloads:

NuPack è disponibile a questo indirizzo http://nupack.codeplex.com/, ASP.NET MVC 3 Beta lo trovate qui e WebMatrix Beta 2 qui.

Buon Download.

various comments edit

images

Venerdì ho ricevuto la mail di conferma da quel pazzo di Alead, che mi comunicava il rinnovo dell’award (in MS ci sono tanti “pazzi” :D).
In ogni caso devo dire che l’emozione è al pari della prima nomina, e spero di poter migliorare contenuti, sia in qualità sia quantità, anche se quest’ultimo periodo non rispetta molto queste parole.
Un super ringraziamento a tutti voi che mi seguite ed i complimenti ai nuovi ed ai rinnovati compagni di avventure.

Ciauz

asp.net comments edit

Tramite il blog di Scott Guthrie, è stata presentata una soluzione temporanea ad un recente problema di vulnerabilità scoperto nel Framework ASP.NET, a partire dalla versione 1.1 fino ad arrivare alla recente 4.0.
La vulnerabilità è piuttosto grave in quanto, in determinate condizioni di configurazione, un utente malintenzionato potrebbe scaricare files contenenti informazioni riservate, come il web.config.
Prima di allarmarsi è necessario capire che il problema è facilmente risolvibile: è possibile proteggere le proprie applicazioni da questo tipo di attacco senza dover ricompilare e/o deployare il sito, ma semplicemente caricando una pagina sul server e modificando il file di configurazione.

Come prima cosa è necessario creare una semplice pagina .aspx “user friendly” contente un messaggio che comunica all’utente il verificarsi di un errore all’interno dell’applicazione, ed inserire il seguente codice direttamente nella pagina stessa:

 

//VB.NET

<%@ Page Language="VB" AutoEventWireup="true" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Threading" %>

<script runat="server">
    Sub Page_Load()
        Dim delay As Byte() = New Byte(0) {}
        Dim prng As RandomNumberGenerator = New RNGCryptoServiceProvider()
        
        prng.GetBytes(delay)
        Thread.Sleep(CType(delay(0), Integer))
        
        Dim disposable As IDisposable = TryCast(prng, IDisposable)
        If Not disposable Is Nothing Then
            disposable.Dispose()
        End If
    End Sub
</script>

<html>
<head runat="server">
    <title>Error</title>
</head>
<body>
    <div>
        Si è verificato un errore
    </div>
</body>
</html>

//C# 

<%@ Page Language="C#" AutoEventWireup="true" %>
<%@ Import Namespace="System.Security.Cryptography" %>
<%@ Import Namespace="System.Threading" %>

<script runat="server">
   void Page_Load() {
      byte[] delay = new byte[1];
      RandomNumberGenerator prng = new RNGCryptoServiceProvider();

      prng.GetBytes(delay);
      Thread.Sleep((int)delay[0]);
        
      IDisposable disposable = prng as IDisposable;
      if (disposable != null) { disposable.Dispose(); }
    }
</script>

<html>
<head runat="server">
    <title>Error</title>
</head>
<body>
    <div>
       Si è verificato un errore
    </div>
</body>
</html>

A questo punto non ci resta che registrare la pagina appena creata all’interno del file di configurazione ed il gioco è fatto.

<configuration>
   <system.web>
     <customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/error.aspx" />
   </system.web>
</configuration>

Per chi fosse interessato alla vulnerabilità, consiglio la lettura del post ufficiale di Scott Guthrie con tutta la spiegazione del problema che trovate qui.