Come ho usato Membership API & Co.

di Alessio Leoncini, in ASP.NET,

Avrei voluto scrivere queste considerazioni quasi un anno fa, poi lavoro ed altre cose mi hanno fatto perdere tempo :) , tuttavia penso che si tratti di un argomento utile a tutti e quindi riprendo da dove avevo lasciato (http://forum.aspitalia.com/forum/post/260048/Profile.aspx#260562) e faccio il punto.

Appena uscita la versione 2.0 del .NET Framework sono rimasto piacevolmente colpito dalle API Membership, Profile e Role che Microsoft aveva deciso di inserire per velocizzare la scrittura dei ripetitivi processi di "gesitone utente" ; leggendo in MSDN e sbirciando con reflector ne ho apprezzato l'implementazione e mi sono fatto subito convincere ad adottarlo immediatamente nel progetto che avevo in corso.

Saltai quasi subito alle conclusioni che Membership doveva gestire l'identita' dell'utente e Profile doveva essere responsabile di tutte le altre informazioni personali , tuttavia non mi piaceva molto che le informazioni di profilo venissero memorizzate nel database in un'unica colonna, e se domani avessi voluto effettuare ricerche per una delle informazioni dell'utente?
Inoltre avevo bisogno di una classe (Entity) User che fosse condivisibile da tutta l'applicazione, ma che fosse ovviamente valorizzata completamente in qualunque contesto.

La soluzione che decisi di adottare fu' la seguente:

- definire le informazioni di profilo attraverso una classe esterna:

<profile defaultProvider="TableProfileProvider" inherits="MyMembershipProvider.Entities.MembershipUserProfile">
...

Tale classe che eredita da ProfileBase , con un metodo statico che restituisce il profilo dato un MembershipUser:

public class MembershipUserProfile : ProfileBase
{
public MembershipUserProfile() { }

public static MembershipUserProfile GetProfile(MembershipUser _pMembershipUser)
{
return ((MembershipUserProfile)(ProfileBase.Create(_pMembershipUser.UserName)));
}

///...
public string FirstName
{
get { return ((string)(this.GetPropertyValue("FirstName"))); }
set { this.SetPropertyValue("FirstName", value); }
}
...

- adottare SqlTableProfileProvider (http://weblogs.asp.net/scottgu/archive/2006/01/10/435038.aspx) l'implementazione del Team di Microsoft del Profile Provider che usa piu' colonne per registrare i dati :

<providers>
<add name="TableProfileProvider"
type="MyMembershipProvider.SqlTableProfileProvider,MyMembershipProvider"
connectionStringName="SqlExpressDB"
table="MyApplication_UsersProfile"
applicationName="MyApplication"/>
</providers>

CREATE TABLE [dbo].[MyApplication_UsersProfile](
[UserId] [uniqueidentifier] NOT NULL,
[FirstName] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[LastName] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[Address] [nvarchar](250) COLLATE Latin1_General_CI_AS NULL,
[City] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[Province] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[ZIP] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[Telephone] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[PortablePhone] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[SMS] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[Fax] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[Email] [nvarchar](50) COLLATE Latin1_General_CI_AS NULL,
[LastUpdatedDate] [datetime] NOT NULL,
CONSTRAINT [PK__MyApplication_UserProfil__628FA481] PRIMARY KEY CLUSTERED
(
[UserId] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

- SqlTableProfileProvider prevedeva pero' la definizione dei membri di Profile solo da web.config, con descrizione del nome di colonna e tipo di dato, ma io avevo bisogno di una classe esterna, quindi decisi di decorare le proprieta' MembershipUserProfile con dei miei attributi che descrivessero il tipo di dato ed il nome di colonna:

[MembershipUserProfileAttribute("FirstName", "nvarchar")]
public string FirstName
{
get { return ((string)(this.GetPropertyValue("FirstName"))); }
set { this.SetPropertyValue("FirstName", value); }
}
.....

/// <summary>
/// Classe per decorazione Proprietà di MembershipUserProfile necessarie a SqlTableProfileProvider
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
internal class MembershipUserProfileAttribute : System.Attribute
{
internal string _ColumnName;
internal string _Datatype;

internal MembershipUserProfileAttribute(string _pColumnName, string _pDatatype)
{
this._ColumnName = _pColumnName;
this._Datatype = _pDatatype;
}

internal string ColumnName
{
get { return _ColumnName; }
}

internal string Datatype
{
get { return _Datatype; }
}
}

per poi usarli dentro SqlTableProfileProvider stesso:
...

//recupero informazioni per il DB da attributi della proprietà
Type clsType = typeof(MembershipUserProfile);
PropertyInfo mInfo = clsType.GetProperty(pp.Name);
MembershipUserProfileAttribute _UserProfileAttribute =

(MembershipUserProfileAttribute)Attribute.GetCustomAttribute(mInfo,typeof(MembershipUserProfileAttribute));

string columnName = _UserProfileAttribute.ColumnName;
SqlDbType datatype = (SqlDbType)Enum.Parse(typeof(SqlDbType), _UserProfileAttribute.Datatype, true);
...

- creare una classe che fondesse i due MembershipUser e MembershipUserProfile , un wrapper appunto:
public class MembershipUserWrapper : MembershipUser
{
private MembershipUserProfile _MembershipUserProfile;

public MembershipUserWrapper(){}

public MembershipUserWrapper(MembershipUser _pMembershipUser) : base(_pMembershipUser.ProviderName,
_pMembershipUser.UserName,
_pMembershipUser.ProviderUserKey,
_pMembershipUser.Email,
_pMembershipUser.PasswordQuestion,
_pMembershipUser.Comment,
_pMembershipUser.IsApproved,
_pMembershipUser.IsLockedOut,
_pMembershipUser.CreationDate,
_pMembershipUser.LastLoginDate,
_pMembershipUser.LastActivityDate,
_pMembershipUser.LastPasswordChangedDate,
_pMembershipUser.LastLockoutDate)
{
this.MembershipUserProfile = MembershipUserProfile.GetProfile(_pMembershipUser);
}

internal MembershipUserProfile MembershipUserProfile
{
get { return _MembershipUserProfile; }
set { _MembershipUserProfile = value; }
}
....

- ed infine scrivere l'ultimo anello della catena, cioé la classe che considero User Entity che non erediti da nessuno (mi sembra di ricordare per problemi di serializzazione) ma con costruttori che possano creare un'istanza completa dell'oggetto.

public class User
{
private Guid _ID;
private string _UserName;
private string _Comment;
private string _Email;
private bool _IsActive;
private bool _IsLockedOut;
private string _FirstName;
private string _LastName;
private string _Address;
private string _City;
private string _Province;
private string _ZIP;
private string _Telephone;
private string _PortablePhone;
private string _SMS;
private string _Fax;
private byte[] _Serialized;
private DateTime _CreationDate;
private DateTime _LastLoginDate;
private DateTime _LastPasswordChangedDate;
private bool _IsOnline;

public User() { }

public User(object _pID) : this(Membership.GetUser(_pID)){ }

public User(string _pUserName) : this(Membership.GetUser(_pUserName)) { }

public User(MembershipUser _pMU) : this(new MembershipUserWrapper(_pMU)) { }

public User(MembershipUserWrapper _pMembershipUserWrapper)
{
_ID = (Guid)_pMembershipUserWrapper.ProviderUserKey;
_UserName = _pMembershipUserWrapper.UserName;
_Comment = _pMembershipUserWrapper.Comment;
_Email = _pMembershipUserWrapper.Email;
_IsActive = _pMembershipUserWrapper.IsActive;
_IsLockedOut = _pMembershipUserWrapper.IsLockedOut;
_FirstName = _pMembershipUserWrapper.FirstName;
_LastName = _pMembershipUserWrapper.LastName;
_Address = _pMembershipUserWrapper.Address;
_City = _pMembershipUserWrapper.City;
_Province = _pMembershipUserWrapper.Province;
_ZIP = _pMembershipUserWrapper.ZIP;
_Telephone = _pMembershipUserWrapper.Telephone;
_PortablePhone = _pMembershipUserWrapper.PortablePhone;
_SMS = _pMembershipUserWrapper.SMS;
_Fax = _pMembershipUserWrapper.Fax;
_CreationDate = _pMembershipUserWrapper.CreationDate;
_LastLoginDate = _pMembershipUserWrapper.LastLoginDate;
_LastPasswordChangedDate = _pMembershipUserWrapper.LastPasswordChangedDate;
}

A distanza di qualche mese e dopo il rilascio da parte di Microsoft del codice sorgente del loro SqlMembershipProvider, mi rendo conto che forse sarebbe stato piú semplice e pulito creare una mia implementazione di SqlMembershipProvider , estendendo peró ProviderBase in quanto MembershipProvider obbliga giá ad usare oggetti MembershipUser, classe che non contiene tutte le informazioni utente di cui ho bisogno.

Spero che le mie (vecchie) considerazioni possano essere di spunto ad altri, sia come strada da seguire o anche no!

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese