Click here to Skip to main content
Click here to Skip to main content

Implementing ASP.NET XML providers - Part 2 (Membership Store)

, 30 Mar 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
In this series I'll go through the steps of my implementation of ASP.NET XmlProviders.

Introduction

The built-in ASP.NET Membership, Roles, Profile providers fits into many Web site use-cases, but not all.
While I was wokring on some of my projects I have experienced the need of having a light ASP.NET providers pack.
Providers that work without database or additional services. Providers that can easy deploy with a Web site under restricted hosting environment.

The obvious solution was to implement ASP.NET Membership, Role, Profile providers base on XML file storages.

In this series I'll go through the steps of my implementation of ASP.NET XmlProviders.

Background

In the first article I have shown you the Persistable base class.
In this artcicle I'm going to cover the usage of the Persistable to implement the XML membership store.
The purpose of XML membership store is to define the membership data which have to be persisted and how to be persisted.
The XML membership store implementation involves next classes: XmlUser, XmlUserStore and XmlUserStore.SynchronizedStore.

XmlUser

XmlUser just defines the data to be persisted per user:

///
<summary>
/// 
/// </summary>
public class XmlUser {

    public Guid UserKey = Guid.Empty;

    public string UserName = string.Empty;
    public string Password = string.Empty;
    public string PasswordSalt = string.Empty;
    public string Email = string.Empty;
    public string PasswordQuestion = string.Empty;
    public string PasswordAnswer = string.Empty;
    public string Comment;
    // track data
    public DateTime CreationDate = DateTime.Now;
    public DateTime LastActivityDate = DateTime.MinValue;
    public DateTime LastLoginDate = DateTime.MinValue;
    public DateTime LastPasswordChangeDate = DateTime.MinValue;
    public DateTime LastLockoutDate = DateTime.MaxValue;
    public bool IsApproved = true;
    public bool IsLockedOut = false;
}

XmlUserStore

XmlUserStore implements Persistable and defines the persistance of all users to XML file:

/// <summary>
/// 
/// </summary>
public partial class XmlUserStore : Persistable<List> {

    #region Properties  /////////////////////////////////////////////////////////////

    /// <summary>
    /// Gets the users.
    /// </summary>
    /// <value>The users.</value>
    public virtual List Users {
        get { return base.Value ?? (base.Value = new List()); }
    }
    #endregion

    #region Construct  //////////////////////////////////////////////////////////////

    /// <summary>
    /// Initializes a new instance of the <see cref="UserStore"/> class.
    /// </summary>
    /// <param name="fileName">Name of the file.</param>
    public XmlUserStore(string fileName)
        : base(fileName) {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="XmlUserStore"/> class.
    /// </summary>
    protected XmlUserStore()
        : base(null) {
    }
    #endregion

    #region Methods /////////////////////////////////////////////////////////////////

    /// <summary>
    /// Gets the user by email.
    /// </summary>
    /// <param name="email">The email.</param>
    /// <returns></returns>
    public virtual XmlUser GetUserByEmail(string email) {

        List users = this.Users;
        return (users != null)
            ? users.Find(delegate(XmlUser user) { return string.Equals(email, user.Email); })
            : null;
    }

    /// <summary>
    /// Gets the user by key.
    /// </summary>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public virtual XmlUser GetUserByKey(Guid key) {

        List users = this.Users;
        return (users != null)
            ? users.Find(delegate(XmlUser user) { return (user.UserKey.CompareTo(key) == 0); })
            : null;
    }

    /// <summary>
    /// Gets the name of the user by.
    /// </summary>
    /// <param name="name">The name.</param>
    /// <returns></returns>
    public virtual XmlUser GetUserByName(string name) {

        List users = this.Users;
        return (users != null)
            ? users.Find(delegate(XmlUser user) { return string.Equals(name, user.UserName); })
            : null;
    }
    #endregion
}

XmlUserStore.SynchronizedStore

SynchronizedStore is a private nested synchronized implementation of XmlUserStore:

public partial class XmlUserStore {

    /// <summary>
    /// 
    /// </summary>
    class SynchronizedStore : XmlUserStore {

        #region Fields  /////////////////////////////////////////////////////////////////

        object _root;
        XmlUserStore _store;

        #endregion

        #region Properties  /////////////////////////////////////////////////////////////

        /// <summary>
        /// Gets a value indicating whether this instance is empty.
        /// </summary>
        /// <value><c>true</c> if this instance is empty; otherwise, <c>false</c>.</value>
        protected override bool IsEmpty {
            get {
                lock (_root) {
                    return _store.IsEmpty;
                }
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance is synchronized.
        /// </summary>
        /// <value>
        ///     <c>true</c> if this instance is synchronized; otherwise, <c>false</c>.
        /// </value>
        public override bool IsSynchronized {
            get {
                return true;
            }
        }

        /// <summary>
        /// Gets the sync root.
        /// </summary>
        /// <value>The sync root.</value>
        public override object SyncRoot {
            get {
                return _root;
            }
        }

        /// <summary>
        /// Gets the users.
        /// </summary>
        /// <value>The users.</value>
        public override List Users {
            get {
                lock (_root) {
                    return _store.Users;
                }
            }
        }

        /// <summary>
        /// Gets or sets the data.
        /// </summary>
        /// <value>The data.</value>
        protected override List Value {
            get {
                lock (_root) {
                    return _store.Value;
                }
            }
            set {
                lock (_root) {
                    _store.Value = value;
                }
            }
        }
        #endregion

        #region Construct  //////////////////////////////////////////////////////////////

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronizedStore"/> class.
        /// </summary>
        /// <param name="store">The store.</param>
        internal SynchronizedStore(XmlUserStore store) {
            _store = store;
            _root = _store.SyncRoot;
        }
        #endregion

        #region Methods /////////////////////////////////////////////////////////////////

        /// <summary>
        /// Deletes the data from the cache and filesystem
        /// </summary>
        /// <returns></returns>
        public override bool Delete() {
            lock (_root) {
                return _store.Delete();
            }
        }

        /// <summary>
        /// Gets the user by email.
        /// </summary>
        /// <param name="email">The email.</param>
        /// <returns></returns>
        public override XmlUser GetUserByEmail(string email) {
            lock (_root) {
                return _store.GetUserByEmail(email);
            }
        }

        /// <summary>
        /// Gets the user by key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public override XmlUser GetUserByKey(Guid key) {
            lock (_root) {
                return _store.GetUserByKey(key);
            }
        }

        /// <summary>
        /// Gets the name of the user by.
        /// </summary>
        /// <param name="name">The name.</param>
        /// <returns></returns>
        public override XmlUser GetUserByName(string name) {
            lock (_root) {
                return _store.GetUserByName(name);
            }
        }

        /// <summary>
        /// Loads the data from the filesystem. For deserialization a XmlSeralizer is used.
        /// </summary>
        /// <returns></returns>
        public override List Load() {
            lock (_root) {
                return _store.Load();
            }
        }

        public override void Save() {
            lock (_root) {
                _store.Save();
            }
        }
        #endregion
    }
}

Synchronized XmlUserStore

Now when we have a synchronized implementation of XmlUser store, we just have to provide the ability to synchronize every instance of XmlUserStore.
The standard pattern to acheave that, is to add a static method Synchronized to the class with argument the instance which have to be synchronized:

#region Static Methods //////////////////////////////////////////////////////////

/// <summary>
/// Synchronizeds the specified store.
/// </summary>
/// <param name="store">The store.</param>
/// <returns></returns>
[HostProtection(SecurityAction.LinkDemand, Synchronization = true)]
public static XmlUserStore Synchronized(XmlUserStore store) {
    if (store == null)
        throw new ArgumentNullException("store");
    return new SynchronizedStore(store);
}
#endregion

Points of Interest

You can find ASP.NET XmlProviders project and all the source code at http://codeplex.com/aspnetxmlproviders

History

  • 30.Mar.2008 - Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Velio Ivanov
Web Developer
Bulgaria Bulgaria
No Biography provided

Comments and Discussions

 
GeneralMessed-up code formatting PinsitebuilderShog92-Apr-08 9:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 30 Mar 2008
Article Copyright 2008 by Velio Ivanov
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid