Il modello basato sui provider è un aspetto molto importante di ASP.NET e rappresenta, da un punto di vista architetturale, una delle novità più interessanti della versione 2.0 del .Net Framework.
Il modello di provider permette agli sviluppatori sfruttare i provider nativi di ASP.NET oppure di sostituirli o migliorarli.
In questo articolo introduciamo il Modello di Provider spiegandone la motivazione di base, la struttura nel dettaglio e, sperimentando in modo diretto, la creazione di un provider di mappe di siti Web personalizzato che ci consente di estrapolare i dati relativi alle mappe dei siti da un database Ms Sql Server anziché da un file XML.
Perché utilizzare i provider?
L'utilizzo dei Provider nasce dalla necessità di disporre di un pattern semplice ed efficace per rendere alcune parti di una applicazione personalizzabili.
ASP .NET 2.0 prevede diversi servizi basati su provider, come ad esempio Membership, Ruoli, Gestione dello stato, Personalizzazione, Mappa e navigazione del sito ed altro; questi servizi utilizzano un API comune per svolgere le diverse operazioni. Questa API, facente parte del sistema di ASP.NET, viene esposta come componente collegabile (dll), in questo modo rimane incapsulata e gli sviluppatori possono utilizzarla senza preoccuparsi di come è strutturata internamente.
Se i provider inclusi nel sistema non sono sufficienti a soddisfare le proprie esigenze, ad esempio se si desidera archiviare i dati relativi allo stato delle sessioni in un database diverso da Ms SQL Server, è possibile implementare provider personalizzati in grado di comunicare con il tipo di database scelto. Il modello è facilmente estendibile, ciascun sviluppatore può implementare nuovi Provider personalizzati o acquistarli da terze parti.
Nel modello di Provider, l'API di livello superiore rimane la stessa mentre l'implementazione è affidata a specifiche classi "Provider"
Un pò di teoria: il design pattern
Dietro al modello di provider c'è il popolare design pattern comportamentale "pattern strategy" appartenente alla famiglia GoF (Gang of Four).
Il design pattern Strategy consiste nella possibilità di rendere intercambiabili tra loro le diverse strategie di implementazione di un algoritmo applicativo. Ogni applicazione, seleziona l'algoritmo più adatto in relazione al particolare contesto senza nessun impatto sull'interfaccia della funzionalità messa a disposizione dalla API di programmazione. Il tutto avviene in maniera del tutto trasparente agli sviluppatori.
Il pattern Strategy prevede, per ogni oggetto, un modo astratto di esporre le proprie funzionalità in modo tale che, un client, può scollegare l'implementazione di default e collegarne una propria.
Gli sviluppatori possono implementare nuovi Provider personalizzati, ridefinire il comportamento dei Provider già inclusi nel sistema, i client possono collegarsi e personalizzare comportamento e impostazioni.
Tutto questo non significa che l'applicazione diventa un progetto "open-source",
ma che è possibile personalizzare alcune parti dell'ambiente di runtime ASP.NET attraverso le classi denominate appunto provider, dalle quali è possibile derivarne una propria.
La pratica
L'implementazione di un proprio provider in ASP.NET prevede la scrittura:
- della classe, che fornisce le funzionalità principali;
- del livello di configurazione, che permette al runtime di ASP.NET di localizzare il provider all'interno dei file di configurazione e di istanziarlo;
- del livello di memorizzazione, che può essere il database, un file XML o qualsiasi altro supporto fisico in cui vengono memorizzati i dati.
Per estendere un provider dobbiamo definire una nuova classe che eredita dalla classe base definita per quel provider. Tutte le classi base provider derivano da una classe base comune denominata Providerbase.
La classe Providerbase
fornisce il metodo Initialize()
che è l'unico metodo comune a tutti i provider.
Il file di configurazione, "web.config", contiene una sezione dedicata ai provider e, attraverso il metodo Initialize()
, le informazioni contenute in questa sezione vengono passate al metodo, cosi, ciascun provider le utilizza per inizializzare il proprio stato.
Modificare il SiteMapProvider
Come abbiamo detto, in ASP.NET i provider sono utilizzati per svolgere diversi compiti, nel nostro articolo prendiamo in considerazione il servizio di mappa del sito web e la relativa classe SiteMapProvider
, la classe base per la gestione delle informazioni della mappa del sito disponibile in ASP.NET.
La mappa di un sito web è una struttura dati gerarchica utilizzata per definire il layout di tutte le pagine dell'applicazione e la relazione che esiste tra le pagine. Il provider di mappa del sito per default, legge i dati relativi alla mappa, da un file XML, denominato web.sitemap
.
Nel nostro esempio, estendiamo il provider predefinito per ottenere un nuovo provider che recupera i dati relativi alla mappa, da un database Ms SQL Server anziché da un file XML e, infine, testeremo il provider.
Le classi derivate dalla classe base del provider SiteMapProvider dovranno creare un albero di oggetti nodo della mappa del sito.
Ciascun nodo contiene:
- nome
- url
- un genitore
- un elenco dei nodi figli
- i ruoli
XmlSiteMapProvider
è il Provider predefinito per la classe SiteMap.
Questo provider deriva dalla classe astratta StaticSiteMapProvider
la cui classe base è la classe SiteMapProvider
.
Listato 1. Le informazioni relative ai provider, nel web.config
<siteMap> Passiamo, ora, alla creazione del provider personalizzato che recupera i dati relativi alle mappe dei siti da un database Ms SQL Server. Supponiamo di avere una tabella contenuta in un database SQL Server denominata SiteMap, ogni record della tabella rappresenta un nodo della mappa del sito. Per semplicità utilizziamo una sola tabella, contenente tre campi: Title, Url, Roles. Creiamo una classe di provider di mappe dei siti personalizzati denominata Apriamo la finestra "Esplora Soluzioni" utilizzando Visual Studio o VWD e aggiungiamo un nuovo elemento nella cartella "App_Code". Nella finestra di dialogo "Aggiungi nuovo elemento" selezioniamo Class come tipo di modello e nominiamo la classe "MySiteMapProvider.cs". Importiamo nella nuova classe i namespaces che ci occorono. Listato 2. I namespace
using System; Nella dichiarazione della classe dobbiamo specificare che Listato 3. Dichiarazione della classe
public class MySiteMapProvider : StaticSiteMapProvider
Aggiungiamo alcuni campi privati . Listato 4. I campi privati
public class MySiteMapProvider : StaticSiteMapProvider Eseguiamo, ora l'override del metodo Listato 5. L'override del metodo Initialize
public override void Initialize (string name, NameValueCollection attributes) Aggiungiamo alla classe il metodo Listato 6. Il Metodo BuildSiteMap
public override SiteMapNode BuildSiteMap () // Eseguo la query per leggere i dati necessari per creare i nodi figli try { Aggiungiamo alla classe il metodo Listato 7. Il Metodo GetRootNode()
protected override SiteMapNode GetRootNodeCore() Ora, per poter utilizzare il provider dobbiamo apportare delle modifiche al "web.config".Apriamo il file e ci assicuriamo di aver inserito e configurato in modo corretto la connessione al database. Listato 8. La stringa di connessione al database
<connectionStrings> All'interno della sezione Listato 8. La sezione <siteMap> all'interno del web.config
<siteMap defaultProvider="MySiteMapProvider" enabled="true"> Passiamo ora, al test della nostra applicazione. Aggiungiamo al progetto una pagina e la chiamiamo "Default.aspx". Dalla "casella degli strumenti" trasciniamo nella pagina l'oggetto Impostiamo quindi le proprietà dei due oggetti. Listato 9. Le proprietà dei due oggetti
<form id="form1" runat="server"> Compiliamo ed eseguiamo l'applicazione. Apparirà la schermata con i dati della mappa del sito letti dalla tabella del database. In questo articolo abbiamo prima introdotto il modello basato sui provider e poi abbiamo visto nel dettaglio, come creare un Site Map Provider personalizzato da utilizzare come provider di default per la creazione della struttura di navigazione del nostro sito web prelevando i dati da un database Ms SQL Server. L'esempio vuole essere un punto di partenza per sperimentare questa tecnica nella personalizzazione dei vari provider inclusi nel sistema quando questi non risultano sufficienti a soddisfare le proprie esigenze.XmlSiteMapProvider
<providers>
<add name="XmlSiteMapProvider " siteMapFile="web.sitemap"
type="System.Web.XmlSiteMapProvider" />
</providers>
</siteMap>
MySiteMapProvider
, che consente di creare la mappa di un sito dai record memorizzati nella tabella SiteMap del database.
using System.Data;
using System.Configuration;
using System.Web;
using System.Data.SqlClient;
using System.Collections.Specialized;
MySiteMapProvider
deriva da System.Web.StaticSiteMapProvider
.
{
static readonly string errore = "Manca la stringa di connessione";
private string strconn = null;
private SiteMapNode root = null;
Initialize()
. Questo metodo inizializza le strutture dati da cui vengono prese le informazioni per la creazione della struttura del provider.
{
base.Initialize (name, attributes);
if (attributes == null)
throw new ConfigurationException (errore);
strconn = attributes["connectionStringName"];
if (String.IsNullOrEmpty(strconn))
throw new ConfigurationException (errore);
}
BuildSiteMap()
di cui eseguiamo l'override. Questo metodo carica le informazioni per la Site Map dal database scelto.
{
// Esco dal metodo se è stato già richiamato e il nodo root non è nullo
if (root != null)
return root;
// Creo il nodo root
root = new SiteMapNode (this, "Default.aspx", "Default.aspx", "Home");
AddNode(root, null);
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["miaconn"].ConnectionString);
connection.Open ();
SqlCommand command = new SqlCommand("SELECT Title, Url, Roles FROM SiteMap", connection);
SqlDataReader reader = command.ExecuteReader ();
int url = reader.GetOrdinal("Url");
int title = reader.GetOrdinal("Title");
int roles = reader.GetOrdinal("Roles");
// Aggiungo i nodi alla mappa del sito
while (reader.Read ()) {
SiteMapNode node = new SiteMapNode (this, reader.GetString(url), reader.GetString(url), reader.GetString(title));
if (!reader.IsDBNull (roles)) {
string rolenames = reader.GetString (roles);
if (!String.IsNullOrEmpty (rolenames)) {
string[] rolelist = rolenames.Split (new char[] { ',', ';' }, 512);
node.Roles = rolelist;
}
}
AddNode (node, root);
}
}
finally { connection.Close (); }
return root;
}
GetRootNodeCore()
. Questo metodo restituisce il nodo principale che gestisce tutti i sotto nodi e, permette, il caricamento della struttura presente nel provider.
{
BuildSiteMap ();
return root;
}
<add name="miaconn"
connectionString="Data Source=.SQLEXPRESS;
AttachDbFilename=C:progettiwww.html.itWebProvidersApp_DataDatabase.mdf;
Integrated Security=True; User Instance=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
del web.config, aggiungiamo il nostro provider nella sezione <siteMap>
e lo impostiamo come provider di default.
<providers>
<add name="MySiteMapProvider"
type="MySiteMapProvider" securityTrimmingEnabled="true"
connectionStringName="Database"/>
</providers>
</siteMap>
SiteMapDataSource
e l'oggetto TreeView
<asp:SiteMapDataSource ID="siteMapDataSource" runat="server" />
<asp:TreeView ID="TreeView1" runat="server" DataSourceID="siteMapDataSource" />
</form>
Conclusioni