Using Additional Data in ASP.NET Membership

Let me welcome you all to a new start to the .NET Nuts & Bolts column here on Codeguru.  You might already know me from the Linked-In .NET (Lidnug) developer group.  I’m Peter Shaw or “Shawty” as most people call me.

I've been involved in IT for over 30 years and have seen the rise from before MS-DOS through to the highly graphical systems we have today. I'm taking over this column on a regular basis, so if you have any subjects you'd like to see covered around the nuts and bolts of .NET, then please feel free to find me in Lidnug, on twitter where I go by the name of @shawty_ds, or you can simply post a comment on one of my articles here on Codeguru.

So without further delay, let's get started:

We all know the score. Websites getting hacked and cracked these days, so people need to use strong passwords, etc. Many of you will have heard (and hopefully listened to the advice) about NOT trying to roll your own security system, and for good reason. There's a very robust and flexible membership service built right into ASP.NET.

This post however is not about how to use the membership system in ASP.NET, but rather how best to attach your own data to it.

Assume, that in addition to storing the usual email, name, password and other regular items, you also want a user’s favourite color and a link to their blog page.

The first thought that likely crosses your mind is to create a profile table. You can then simply put these items into that table and link to them by the ID provided to you by ASP.NET membership.

For one or two small items, that might be overkill.  Think about it, you need to make the classes, make the tables, set up and use an ORM and a lot of other stuff, just to add an additional couple of strings.

There is however, another way.  You can quite easily extend the profile base used by the membership system; what this does is give you a first class object orientated way to attach extra data to your membership record.

This data is then loaded and saved, and persisted by the ASP.NET framework right along with your normal attributes that you expect to be there.

The first thing you need to do is to configure your apps web.config or app.config files as follows:

<membership defaultProvider="MembershipProvider">
  <providers>
    <clear />
    <add name="MembershipProvider"
         type="System.Web.Security.SqlMembershipProvider"
         connectionStringName="myconnectionstring"
         enablePasswordRetrieval="false"
         enablePasswordReset="true"
         requiresQuestionAndAnswer="false"
         requiresUniqueEmail="false"
         maxInvalidPasswordAttempts="5"
         minRequiredPasswordLength="4"
         minRequiredNonalphanumericCharacters="0"
         passwordAttemptWindow="10"
         applicationName="ctfApplication" />
  </providers>
</membership>
 
<profile enabled="true" defaultProvider="ProfileProvider" inherits="webui.Models.UserProfile">
  <providers>
    <clear />
    <add name="ProfileProvider"
         connectionStringName="myconnectionstring"
         type="System.Web.Profile.SqlProfileProvider, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
         applicationName="ctfApplication" />
  </providers>
</profile>

This will configure .NET to use the profile service built into ASP.NET membership.

You should note some things in the above XML. The 'connectionStringName' is the name of a regular .NET connection string and can be the name of any connection string that is placed in your applications connection string pool.  This generally will be the same database that the membership system uses for its regular user and password tables.

You can point this to a different database, but you must absolutely make sure that the database has been set up and populated with the correct tables as needed to use a normal .NET membership set-up. You should note that in the 'profile' tag there is an attribute called 'inherits', this MUST point to a fully pathed and name spaced object within your app that will handle the extra data you want to use.

The next thing you need to do, is to actually add your user profile model (the one specified in the inherits tag) to your project.  For the purposes if this demo, I'm adding it to the models folder of an ASP.NET MVC Application.

Your profile model should look something like the following:

using System.Web.Profile;
using System.Web.Security;
 
namespace webui.Models
{
  public class UserProfile : ProfileBase
  {
    [SettingsAllowAnonymous(false)]
    public string FavouriteColour
    {
      get { return base["FavouriteColour"] as string; }
      set { base["FavouriteColour"] = value; }
    }
 
    [SettingsAllowAnonymous(false)]
    public string BlogPage
    {
      get { return base["BlogPage"] as string; }
      set { base["BlogPage"] = value; }
    }
 
    public static UserProfile GetProfileForUser(string userName)
    {
      var membershipUser = Membership.GetUser(userName);
      if (membershipUser != null)
      {
        return Create(membershipUser.UserName) as UserProfile;
      }
      return null;
    }
 
    public static UserProfile CurrentUser
    {
      get
      {
        var membershipUser = Membership.GetUser();
        if (membershipUser != null)
        {
          return Create(membershipUser.UserName) as UserProfile;
        }
        return null;
      }
    }
 
    public static UserProfile NewUser
    {
      get { return System.Web.HttpContext.Current.Profile as UserProfile; }
    }
 
  }
}

Remember, this model has to be in the exact namespace and path specified in the XML config file to work correctly.

The static members on the end of the class are there to make working with the object much easier, and act as a kind of helper when marshalling the data.

At this point, you could actually just use that class as is if you wanted to, as well as having the usual .NET membership stuff available; you'll find that you also have your extra two fields there too, and when you new it up using the static helpers, you should have normal property access to them.

However, I prefer to go one step further.

First, I'll create a proper view model for my data:

using System.ComponentModel.DataAnnotations;
 
namespace webui.Models
{
  public class MemberData
  {
 
    [Required(ErrorMessage = "Username is required")]
    [Display(Name = "User Name")]
    [StringLength(50, MinimumLength = 3)]
    public string UserName { get; set; }
 
    [Required(ErrorMessage = "First Name is required")]
    [Display(Name = "First Name")]
    [StringLength(50, MinimumLength = 3)]
    public string FirstName { get; set; }
  }
}

You can if you wish extend this to hold other normal membership data too, but I've left that out for clarity.

Then build yourself a library of static functions to help ease the pain of working with the objects you just created.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using webui.Models;
 
namespace webui.Classes
{
  public static class MembershipUtils
  {
    public static MemberData GetMemberDataForCurrentUser()
    {
      MembershipUser membershipUser = Membership.GetUser();
      if (membershipUser != null)
      {
        string userName = membershipUser.UserName;
        return GetMemberDataFor(userName);
      }
      return null;
    }
 
    public static MemberData GetMemberDataFor(string userName)
    {
      MemberData result = new MemberData();
 
      MembershipUser user = Membership.GetUser(userName);
      if (user != null)
      {
        UserProfile pc = UserProfile.GetProfileForUser(userName);
 
        result.FavouriteColour = user.FavouriteColour;
        result.BlogPage = pc.BlogPage;
 
      }
      else
      {
        result = new UserProfile();
      }
 
      return result;
    }
 
    public static void UpdateMemberData(MemberData memberData)
    {
      UserProfile pc = UserProfile.GetProfileForUser(memberData.UserName);
 
      pc.FavouriteColour = memberData.FavouriteColour;
      pc.BlogPage = memberData.BlogPage;
 
      pc.Save();
 
    }
 
    public static void AddNewUser(AddNewUserViewModel newUserDetails)
    {
      MembershipUser newUser = Membership.CreateUser(
        newUserDetails.UserName,
        newUserDetails.Password,
        newUserDetails.EmailAddress);
      MemberData memberData = GetMemberDataFor(newUserDetails.UserName);
      memberData. FavouriteColour = newUserDetails.FavouriteColour;
      memberData.BlogPage = newUserDetails.BlogPage;
      UpdateMemberData(memberData);
    }
 
    public static void ChangeUserFavoriteColour(string colour)
    {
      MemberData memberData = GetMemberDataForCurrentUser();
      memberData.FavouriteColour = colour;
      UpdateMemberData(memberData);
    }
 
    public static void ChangeUserBlogPage(string blogPage)
    {
      MemberData memberData = GetMemberDataForCurrentUser();
      memberData.BlogPage = blogPage;
      UpdateMemberData(memberData);
    }
 
  }
}
 

Notice in the “add new user” function that I do have a custom view model that I've not shown here.  This is just a simple model containing all the data from the view such as user name, password, etc.; all stuff that should be very easy to do.

In Summary

Adding extra fields to an ASP.NET membership set-up doesn't have to be difficult, and if there is only a small amount of data, then it's worth doing it this way rather than having to create an entire model and relationships.  This method can also be used to carry a tag, which is then used to access other tables with a richer amount of information, should you need it.

If you have any ideas for posts you'd like to see here, please drop me a note on Twitter (@shawty_ds) or via my G+ and/or Linked-in pages.



Related Articles

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Packaged application development teams frequently operate with limited testing environments due to time and labor constraints. By virtualizing the entire application stack, packaged application development teams can deliver business results faster, at higher quality, and with lower risk.

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds