Export (0) Print
Expand All

How to: Implement a Custom Membership User

Provides an example that illustrates how to extend the MembershipUser class with a custom membership provider.

While the user System.Web.Profile provides a convenient mechanism for storing information per user in a Web application, the design of your application might require that additional user information be stored with the user authentication information in the Membership data store. In this case, you would need to build a custom membership provider to store and retrieve the user authentication information and the additional user values in your data store (for an example of a custom membership provider, see Implementing a Membership Provider). Additionally, you can extend the MembershipUser class to make the added user values available to application code.

Creating a custom membership user involves the following tasks:

  • Create a class that inherits the MembershipUser class.

  • Create a data source to store authentication information and additional user settings.

  • Create a custom membership provider for the data store. The custom membership provider will contain additional code that can receive objects of the custom membership user type as input, as well as return objects of the custom membership user type.

The examples in this topic show how you can modify the custom membership provider example in How to: Sample Membership Provider Implementation to support a custom membership user implementation.

You can create a custom membership user by creating a class that inherits the MembershipUser class, and then including properties that expose the additional user values. Optionally, you can add methods and events to the MembershipUser class as well.

When the Membership class is called to create an instance of your custom MembershipUser, only the constructors defined by the MembershipUser class will be called. If your MembershipUser implementation includes additional constructor overloads, those constructors will only be called by application code that is written specifically to call a custom constructor.

The following code example shows a simple custom membership user that inherits the MembershipUser class and provides two additional properties: IsSubscriber, which is a Boolean property that identifies whether the user subscribes to a service or newsletter for a Web application; and CustomerID, which contains a unique identifier for a separate customer database.

using System;
using System.Web.Security;

namespace Samples.AspNet.Membership.CS
{
    public class OdbcMembershipUser : MembershipUser
    {
        private bool _IsSubscriber;
        private string _CustomerID;

        public bool IsSubscriber
        {
            get { return _IsSubscriber; }
            set { _IsSubscriber = value; }
        }

        public string CustomerID
        {
            get { return _CustomerID; }
            set { _CustomerID = value; }
        }

        public OdbcMembershipUser(string providername,
                                  string username,
                                  object providerUserKey,
                                  string email,
                                  string passwordQuestion,
                                  string comment,
                                  bool isApproved,
                                  bool isLockedOut,
                                  DateTime creationDate,
                                  DateTime lastLoginDate,
                                  DateTime lastActivityDate,
                                  DateTime lastPasswordChangedDate,
                                  DateTime lastLockedOutDate,
                                  bool isSubscriber,
                                  string customerID) :
                                  base(providername,
                                       username,
                                       providerUserKey,
                                       email,
                                       passwordQuestion,
                                       comment,
                                       isApproved,
                                       isLockedOut,
                                       creationDate,
                                       lastLoginDate,
                                       lastActivityDate,
                                       lastPasswordChangedDate,
                                       lastLockedOutDate)
        {
            this.IsSubscriber = isSubscriber;
            this.CustomerID = customerID;
        }



    }
}


For an example of modifying the CreateUserWizard control to include additional user information for a membership user, see How to: Customize the ASP.NET CreateUserWizard Control.

You will need to provide a data store for the user authentication information for the membership feature, as well as the additional user information for your custom membership user.

The following code example shows a query that you can run in a Microsoft Access database to create a table to store authentication information and property values for your custom membership user.

CREATE TABLE Users
(
  PKID Guid NOT NULL PRIMARY KEY,
  Username Text (255) NOT NULL,
  ApplicationName Text (255) NOT NULL,
  Email Text (128) NOT NULL,
  Comment Text (255),
  Password Text (128) NOT NULL,
  PasswordQuestion Text (255),
  PasswordAnswer Text (255),
  IsApproved YesNo, 
  LastActivityDate DateTime,
  LastLoginDate DateTime,
  LastPasswordChangedDate DateTime,
  CreationDate DateTime, 
  IsOnLine YesNo,
  IsLockedOut YesNo,
  LastLockedOutDate DateTime,
  FailedPasswordAttemptCount Integer,
  FailedPasswordAttemptWindowStart DateTime,
  FailedPasswordAnswerAttemptCount Integer,
  FailedPasswordAnswerAttemptWindowStart DateTime,
  IsSubscriber YesNo,
  CustomerID Text (64)
)

You will need to create a custom membership provider that supports both your custom membership user type, and your custom membership data store. The GetUser and CreateUser methods of the custom membership provider can be written to return objects of the custom membership user type. The UpdateUser method of the custom membership provider can be written to receive an object of the custom membership user type as input.

The following sections provide guidance on creating a custom membership provider that uses a custom membership user type. The examples build on the code provided in How to: Sample Membership Provider Implementation and use the database schema from the Create a Data Source for the Membership User Data section earlier in this topic.

Modify the GetUser Methods

When working with a custom membership user type, the MembershipProvider.GetUser and MembershipProvider.GetUser methods of your membership provider must still return an object of type MembershipUser. Provided your custom membership user class inherits the MembershipUser class, return an object of your custom membership user type as the return value for your implementation of the GetUser methods. Application code can then cast the returned MembershipUser as your custom membership user type to access the additional members of your custom membership user as shown in the following code example.

The following code example shows the modified GetUser methods (and their supporting private method) of the sample membership provider from How to: Sample Membership Provider Implementation, which have been updated to return the custom membership user type from the Create a Custom Membership User section earlier in this topic.

//
// MembershipProvider.GetUser(string, bool)
//

public override MembershipUser GetUser(string username, bool userIsOnline)
{
   OdbcConnection conn = new OdbcConnection(connectionString);
   OdbcCommand cmd = new OdbcCommand("SELECT PKID, Username, Email, PasswordQuestion," +
        " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
        " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate," +
        " IsSubscriber, CustomerID" +
        " FROM Users  WHERE Username = ? AND ApplicationName = ?", conn);

  cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
  cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = pApplicationName;

  OdbcMembershipUser u = null;
  OdbcDataReader reader = null;

  try
  {
    conn.Open();

    reader = cmd.ExecuteReader();

    if (reader.HasRows)
    {
      reader.Read();
      u = GetUserFromReader(reader);

      if (userIsOnline)
      {
        OdbcCommand updateCmd = new OdbcCommand("UPDATE Users  " +
                  "SET LastActivityDate = ? " +
                  "WHERE Username = ? AND Applicationname = ?", conn);

        updateCmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = DateTime.Now;
        updateCmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
        updateCmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = pApplicationName;

        updateCmd.ExecuteNonQuery();
      }
    }

  }
  catch (OdbcException e)
  {
    if (WriteExceptionsToEventLog)
    {
      WriteToEventLog(e, "GetUser(String, Boolean)");

      throw new ProviderException(exceptionMessage);
    }
    else
    {
      throw e;
    }
  }
  finally
  {
    if (reader != null) { reader.Close(); }

    conn.Close();
  }

  return u;      
}


//
// MembershipProvider.GetUser(object, bool)
//

public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
  OdbcConnection conn = new OdbcConnection(connectionString);
  OdbcCommand cmd = new OdbcCommand("SELECT PKID, Username, Email, PasswordQuestion," +
        " Comment, IsApproved, IsLockedOut, CreationDate, LastLoginDate," +
        " LastActivityDate, LastPasswordChangedDate, LastLockedOutDate," +
        " IsSubscriber" +
        " FROM Users  WHERE PKID = ?", conn);

  cmd.Parameters.Add("@PKID", OdbcType.UniqueIdentifier).Value = providerUserKey;

  OdbcMembershipUser u = null;
  OdbcDataReader reader = null;

  try
  {
    conn.Open();

    reader = cmd.ExecuteReader();

    if (reader.HasRows)
    {
      reader.Read();
      u = GetUserFromReader(reader);

      if (userIsOnline)
      {
        OdbcCommand updateCmd = new OdbcCommand("UPDATE Users  " +
                  "SET LastActivityDate = ? " +
                  "WHERE PKID = ?", conn);

        updateCmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = DateTime.Now;
        updateCmd.Parameters.Add("@PKID", OdbcType.UniqueIdentifier).Value = providerUserKey;

        updateCmd.ExecuteNonQuery();
      }
    }

  }
  catch (OdbcException e)
  {
    if (WriteExceptionsToEventLog)
    {
      WriteToEventLog(e, "GetUser(Object, Boolean)");

      throw new ProviderException(exceptionMessage);
    }
    else
    {
      throw e;
    }
  }
  finally
  {
    if (reader != null) { reader.Close(); }

    conn.Close();
  }

  return u;      
}


//
// GetUserFromReader
//    A helper function that takes the current row from the OdbcDataReader
// and hydrates a MembershipUser from the values. Called by the 
// MembershipUser.GetUser implementation.
//

private OdbcMembershipUser GetUserFromReader(OdbcDataReader reader)
{
  object providerUserKey = reader.GetValue(0);
  string username = reader.GetString(1);
  string email = reader.GetString(2);

  string passwordQuestion = "";
  if (reader.GetValue(3) != DBNull.Value)
    passwordQuestion = reader.GetString(3);

  string comment = "";
  if (reader.GetValue(4) != DBNull.Value)
    comment = reader.GetString(4);

  bool isApproved = reader.GetBoolean(5);
  bool isLockedOut = reader.GetBoolean(6);
  DateTime creationDate = reader.GetDateTime(7);

  DateTime lastLoginDate = new DateTime();
  if (reader.GetValue(8) != DBNull.Value)
    lastLoginDate = reader.GetDateTime(8);

  DateTime lastActivityDate = reader.GetDateTime(9);
  DateTime lastPasswordChangedDate = reader.GetDateTime(10);

  DateTime lastLockedOutDate = new DateTime();
  if (reader.GetValue(11) != DBNull.Value)
    lastLockedOutDate = reader.GetDateTime(11);

  bool isSubscriber = false;
  if (reader.GetValue(12) != DBNull.Value)
    isSubscriber = reader.GetBoolean(12);

  string customerID = String.Empty;
  if (reader.GetValue(13) != DBNull.Value)
    customerID = reader.GetString(13);        

  OdbcMembershipUser u = new OdbcMembershipUser(this.Name,
                                        username,
                                        providerUserKey,
                                        email,
                                        passwordQuestion,
                                        comment,
                                        isApproved,
                                        isLockedOut,
                                        creationDate,
                                        lastLoginDate,
                                        lastActivityDate,
                                        lastPasswordChangedDate,
                                        lastLockedOutDate,
                                        isSubscriber,
                                        customerID);

  return u;
}


Modify the UpdateUser Method

When working with a custom membership user type and a custom membership provider, implement an UpdateUser method that takes an object of type MembershipUser as input. In your implementation of the UpdateUser method, cast the supplied MembershipUser object as your custom membership user type to access the values of the additional properties and update them in the data store.

The following code example shows the modified UpdateUser method of the sample membership provider from How to: Sample Membership Provider Implementation, which has been updated to cast the supplied user as the custom membership user type from the Create a Custom Membership User section earlier in this topic.

public override void UpdateUser(MembershipUser user)
{
  OdbcConnection conn = new OdbcConnection(connectionString);
  OdbcCommand cmd = new OdbcCommand("UPDATE Users " +
          " SET Email = ?, Comment = ?," +
          " IsApproved = ?, IsSubscriber = ?, CustomerID = ?" +
          " WHERE Username = ? AND ApplicationName = ?", conn);

  OdbcMembershipUser u = (OdbcMembershipUser)user;

  cmd.Parameters.Add("@Email", OdbcType.VarChar, 128).Value = user.Email;
  cmd.Parameters.Add("@Comment", OdbcType.VarChar, 255).Value = user.Comment;
  cmd.Parameters.Add("@IsApproved", OdbcType.Bit).Value = user.IsApproved;
  cmd.Parameters.Add("@IsSubscriber", OdbcType.Bit).Value = u.IsSubscriber;
  cmd.Parameters.Add("@CustomerID", OdbcType.VarChar, 128).Value = u.CustomerID;
  cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = user.UserName;
  cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = pApplicationName;


  try
  {
    conn.Open();

    cmd.ExecuteNonQuery();
  }
  catch (OdbcException e)
  {
    if (WriteExceptionsToEventLog)
    {
      WriteToEventLog(e, "UpdateUser");

      throw new ProviderException(exceptionMessage);
    }
    else
    {
      throw e;
    }
  }
  finally
  {
    conn.Close();
  }
}


Modify the CreateUser Method

When working with a custom membership user type and a custom membership provider, the custom membership provider must implement a CreateUser method that takes only the properties supported by the MembershipUser class as input. You can create an overload of the CreateUser method that takes additional property values as shown in the following code example.

However, this overload will not be called by the Membership class or controls that rely on the Membership class, such as the CreateUserWizard control. To call this method from an application, cast the MembershipProvider instance referenced by the Membership class as your custom membership provider type, and then call your CreateUseroverload directly.

If your application is using the CreateUserWizard control to add new users to your membership data source, you can customize the wizard steps of the CreateUserWizard control to include controls that retrieve the additional property values of your custom membership user. You can then handle the CreatedUser event of the CreateUserWizard control and add event code that does the following:

  • Retrieves the property values of the additional membership user.

  • Casts the membership user created by the CreateUserWizard control as your custom membership user type.

  • Sets the additional properties on the membership user.

  • Passes the updated user to the UpdateUser method of the Membership class. This will call the UpdateUser method of your custom provider (which is described in the Modify the UpdateUser Method section earlier in this topic) to add the additional property values to your data source.

NoteNote:

For an example of modifying the steps of the CreateUserWizard, see How to: Customize the ASP.NET CreateUserWizard Control.

The following code example shows the modified CreateUser method of the sample membership provider from How to: Sample Membership Provider Implementation, which has been updated to return the custom membership user type from the Create a Custom Membership User section earlier in this topic. An overload has been created to take values for the additional properties of the custom membership provider as input.

//
// MembershipProvider.CreateUser
//

public override MembershipUser CreateUser(string username,
           string password,
           string email,
           string passwordQuestion,
           string passwordAnswer,
           bool isApproved,
           object providerUserKey,
           out MembershipCreateStatus status)
{
  return this.CreateUser(username, password, email,
                        passwordQuestion, passwordAnswer,
                        isApproved, providerUserKey, false, "",
                        out status);
}


//
// OdbcMembershipProvider.CreateUser -- returns OdbcMembershipUser
//

public OdbcMembershipUser CreateUser(
         string username,
         string password,
         string email,
         string passwordQuestion,
         string passwordAnswer,
         bool isApproved,
         object providerUserKey,
         bool isSubscriber,
         string customerID,
         out MembershipCreateStatus status)
{
  ValidatePasswordEventArgs args = 
    new ValidatePasswordEventArgs(username, password, true);

  OnValidatingPassword(args);

  if (args.Cancel)
  {
    status = MembershipCreateStatus.InvalidPassword;
    return null;
  }

  if (RequiresUniqueEmail && GetUserNameByEmail(email) != "")
  {
    status = MembershipCreateStatus.DuplicateEmail;
    return null;
  }

  MembershipUser u = GetUser(username, false);

  if (u == null)
  {
    DateTime createDate = DateTime.Now;

    if (providerUserKey == null)
    {
      providerUserKey = Guid.NewGuid();
    }
    else
    {
      if ( !(providerUserKey is Guid) )
      {
        status = MembershipCreateStatus.InvalidProviderUserKey;
        return null;
      }
    }

    OdbcConnection conn = new OdbcConnection(connectionString);
    OdbcCommand cmd = new OdbcCommand("INSERT INTO Users " +
          " (PKID, Username, Password, Email, PasswordQuestion, " +
          " PasswordAnswer, IsApproved," +
          " Comment, CreationDate, LastPasswordChangedDate, LastActivityDate," +
          " ApplicationName, IsLockedOut, LastLockedOutDate," +
          " FailedPasswordAttemptCount, FailedPasswordAttemptWindowStart, " +
          " FailedPasswordAnswerAttemptCount, FailedPasswordAnswerAttemptWindowStart, " +
          " IsSubscriber, CustomerID)" +
          " Values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", conn);

    cmd.Parameters.Add("@PKID", OdbcType.UniqueIdentifier).Value = providerUserKey;
    cmd.Parameters.Add("@Username", OdbcType.VarChar, 255).Value = username;
    cmd.Parameters.Add("@Password", OdbcType.VarChar, 255).Value = EncodePassword(password);
    cmd.Parameters.Add("@Email", OdbcType.VarChar, 128).Value = email;
    cmd.Parameters.Add("@PasswordQuestion", OdbcType.VarChar, 255).Value = passwordQuestion;
    cmd.Parameters.Add("@PasswordAnswer", OdbcType.VarChar, 255).Value = EncodePassword(passwordAnswer);
    cmd.Parameters.Add("@IsApproved", OdbcType.Bit).Value = isApproved;
    cmd.Parameters.Add("@Comment", OdbcType.VarChar, 255).Value = "";
    cmd.Parameters.Add("@CreationDate", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@LastPasswordChangedDate", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@LastActivityDate", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@ApplicationName", OdbcType.VarChar, 255).Value = pApplicationName;
    cmd.Parameters.Add("@IsLockedOut", OdbcType.Bit).Value = false;
    cmd.Parameters.Add("@LastLockedOutDate", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@FailedPasswordAttemptCount", OdbcType.Int).Value = 0;
    cmd.Parameters.Add("@FailedPasswordAttemptWindowStart", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@FailedPasswordAnswerAttemptCount", OdbcType.Int).Value = 0;
    cmd.Parameters.Add("@FailedPasswordAnswerAttemptWindowStart", OdbcType.DateTime).Value = createDate;
    cmd.Parameters.Add("@IsSubscriber", OdbcType.Bit).Value = isSubscriber;
    cmd.Parameters.Add("@CustomerID", OdbcType.VarChar, 128).Value = customerID;

    try
    {
      conn.Open();

      int recAdded = cmd.ExecuteNonQuery();

      if (recAdded > 0)
      {
        status = MembershipCreateStatus.Success;
      }
      else
      {
        status = MembershipCreateStatus.UserRejected;
      }
    }
    catch (OdbcException e)
    {
      if (WriteExceptionsToEventLog)
      {
        WriteToEventLog(e, "CreateUser");
      }

      status = MembershipCreateStatus.ProviderError;
    }
    finally
    {
      conn.Close();
    }


    return (OdbcMembershipUser)GetUser(username, false);      
  }        
  else
  {
    status = MembershipCreateStatus.DuplicateUserName;
  }


  return null;
}


Community Additions

ADD
Show:
© 2014 Microsoft