Stamane nel newsgroup di asp.net è apparsa una domanda interessante che mi ha fatto pensare un po' a proposito delle potenzialità di Membership e di ProfileProvider. Ho colto così l'occasione per scrivere due righe di codice per risolvere un problema che effettivamente potrebbe essere limitante nell'uso di questi strumenti. Il problema in questione è che così come le informazioni del profilo vengono persistite nella relativa tabella è praticamente impossibile se non molto difficile accedere ad esse per filtrare gli utenti in base ad esse.

Le informazioni di profilo vengono memorizzate tutte in due campi nella tabella aspnet_Profile con un formato particolare che consente al runtime di recuperarle velocemente ma che d'altro canto impedisce di accedervi per mezzo di una semplice query. Ecco come:

   1: PropertyNames: Age:S:0:2:Province:S:2:2:Town:S:4:7:
   2: PropertyValues: 38TVTreviso

In sostanza il campo PropertyNames oltre al nome delle proprietà contiene il tipo, il punto di inizio e la lunghezza della frazione del campo PropertyValues. Nel riquadro è riportato il formato di tre proprieta: Age, Province e Town.

Ora è del tutto evidente che sperare di scrivere una query SQL che consenta di raggiungere ad esempio tutti gli utenti che abitano in provincia di Treviso è pura utopia. Inoltre se andiamo a vedere i metodi del ProfileProvider in breve ci renderemo conto che estrarre questa informazione è piuttosto complicato e rischia di costringerci a leggere i campi uno per uno decodificandoli.

Oggi qualcuno naturalmente aveva bisogno di ottenere proprio questo risultato e quindi mi sono dovuto inventare un workaround. Di per se non sarebbe complesso trovare gli utenti di Treviso se i dati fossero spalmati correttamente in una tabella, perciò ho pensato di salvare una copia dei dati di interesse in una tabella di appoggio da usare per questi scopi.

L'idea è quella di inserirsi al punto giusto ed operare inserimenti e update all'unisono con il provider stesso in modo da tenere allineata questa tabella senza però perdere le funzionalità comode del provider che deve rimanere l'unico gestore in lettura e scrittura. Quindi ho deciso di estendere il provider e fare l'override del metodo SetPropertyValues().

   1: public override void SetPropertyValues(
   2:     SettingsContext sc, 
   3:     SettingsPropertyValueCollection properties)
   4: {
   5:     base.SetPropertyValues(sc, properties);
   6:  
   7:     using (SqlConnection conn = new SqlConnection(this.ConnectionString))
   8:     {
   9:         conn.Open();
  10:  
  11:         using (SqlCommand command = new SqlCommand("spx_UpdateProfileIndex", conn))
  12:         {
  13:             command.CommandType = CommandType.StoredProcedure;
  14:             command.Parameters.AddWithValue("@username", sc["UserName"]);
  15:             command.Parameters.AddWithValue("@province", properties["Province"].PropertyValue);
  16:             command.ExecuteNonQuery();
  17:         }
  18:     }
  19: }

Nel metodo dapprima si chiama la base che provvede ad inserire i dati al loro posto come consueto. Poi il controllo passa alla stored procedure nella quale dati che vengono salvati nella tabella di appoggio. La tabella in questione ha un campo per ogni proprietà. La stored procedure è abbastanza furba da sapere se deve fare una insert o un update.

Con questo sistema si riesce a creare quindi una tabellina semplice che ci aiuti a fare delle query per trovare il riferimento agli utenti che hanno un dato valore in una proprietà. L'importante è non abusarne. Non si deve nemmeno per un momento pensare che modificando un valore in essa si modificheranno in accordo anche il valore della proprietà di profile, altrimenti ci troveremmo nella condizione in cui rifare il provider è più conveniente. Ovviamente poi sarà nostro onere tenere il numero di colonne appropriato per proprietà che vengano aggiunte in futuro.

Tuttavia lasciatemi fare una considerazione: la palese inadeguateza del ProfileProvider in casi come questo mi suggerisce che sia preferibile adottare un sistema custom per la persistenza di proprietà che poi servano per catalogare gli utenti, mentre il ProfileProvider dovrebbe essere usato per dati meno sensibili come ad esempio le personalizzazione dell'interfaccia o comunque dei dati che devono solamente essere persistiti tra diverse sessioni.

Codice di esempio completo: (6,8 KB)


Commenti (1) -

# | Davide Mauri | 17.07.2007 - 17.53

Ciao Andrea, mi sono scontrato anche io con quell'oscenità del database ASPNETDB e della tabella aspnet_Profile. Nel caso possa essere utile anche ad altre persone lascio il link al mio post:

http://community.ugiss.org/blogs/dmauri/archive/2007/07/16/i-grossi-problemi-di-un-db-non-normalizzato-aspnet-profile.aspx

che contiene anche una soluzione per poter estrarre i dati dei profile direttamente da T-SQL

Ciao!

Aggiungi Commento