I have a requirement for my client that I’m supposed to allow users to authenticate using Forms Based Authentication. However, they also want users to be able to enter supplemental information such as their address, phone number, etc. Typically, this can be done rather easily using the ASP.NET Profile infrastructure.

However, an ASP.NET application has dynamically generated code that gets generated when the application is compiled on the fly. As you know, in SharePoint most all code has to be compiled ahead of time in order to run. Because of this, it becomes necessary to use strongly typed objects, and to reference these Types with their full namespace names, assembly references, etc., in the web.config. Unfortunately, I had to spend hours trying to dig through the Internet trying to look at ASP.NET examples and extrapolate how the code would have to be modified for a SharePoint environment.

There’s a legend that goes something like this:
The cathedral in Florence had been built without a dome because the people of the city knew that they would one day have the know-how to build the world’s largest dome — they just didn’t have it yet. Finally, when everything was completed but the dome, the people of the city held a competition to see who could build it. The competition required different architects to stand a piece of egg upright on a stone. According to the art historian Vasari, the architect Brunelleschi “…giving one end a blow on the flat piece of marble, made it stand upright. The architects protested that they could have done the same; but Filippo answered, laughing, that they could have made the dome, if they had seen his design.”

In much the same way, the final solution doesn’t seem that hard, but it took me quite a while to figure out how all the pieces fit together!

As a starting point, I was using the Forms Based Authentication module of the Community Kit for SharePoint, found on CodePlex. This code helped me do the work of allowing users to sign up, to edit their username and e-mail, add them to groups, etc. However, I wanted to be able to use the Profile table in the ASP.NET generated database I was already using to store user information.

In a vanilla ASP.NET application, I could simply type in “Profile.” in my code-behind and Intellisense would automatically access profile properties I had defined in my web.config. However, this is available because the ASP.NET engine automatically compiles a class called “ProfileCommon” which it instantiates as an object called “Profile”. To a developer, it all seems to work “automagically”. Not so for the poor SharePoint developer. Because we don’t get the benefit of any dynamically generated code, we need to write our own version of the “ProfileCommon” class, and add it to our solution.

At first, it’s a pretty basic class. It only has one static method, which is used to generate a Profile object. It inherits from the System.Web.Profile.ProfileBase object.

using System.Web.Profile;

namespace BB.FBA
{
    public class UserProfile : ProfileBase
    {
        public static UserProfile GetProfile(string username)
        {
            return Create(username) as UserProfile;
        }
     }
}

The next thing I need to do is specify in my web.config that my profile is inheriting from this custom class. So, I add “inherits=BB.FBA.UserProfile” to my <profile> node. My final node looks something like this:

<profile defaultProvider="SqlProfileProvider" enabled="true" inherits="BB.FBA.UserProfile">
  <providers>
    <clear />
    <add name="SqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="FBAConnectionString" applicationName="/" />
  </providers>
  
  <properties>
  </properties>
</profile>

(I’ve set up my profile provider and connection string nodes according to regular Forms Based Authentication standards. You read about how to do set up your FBA site here.)

This seems pretty straight forward, but I ran into a problem. Again, because we don’t have the benefit of classes being compiled at run-time, I got an error saying it couldn’t find custom profile provider class. What I had to do was to add a reference to my dll in the node in my web.config, like this:

<add assembly="BB.FBA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8f1s7b751a09c4e0" />

Once I added that, the application could find my custom UserProfile class.

Next, I wanted to create a strongly typed object to store user information, rather than adding a million <property> nodes to my web.config. So, I created a “Person” class. For the sake of brevity, I’ve shortened it:

namespace BB.FBA
{
    public class Person
    {
        public Person()
        {
        }
        string phone = "";
        public string Phone
        {
            get { return phone; }
            set { phone = value; }
        }
        string fax = "";
        public string Fax
        {
            get { return fax; }
            set { fax = value; }
        }
    }
}

The next thing I needed to do was add it to my UserProfile class, so my new “Person” object could be accessed through it.

[SettingsAllowAnonymous(true)]
public Person Person
{
    get
    {
        return (base.GetPropertyValue("Person") as Person);
     }
     set
     {
        base.SetPropertyValue("Person", value);
     }
}

(I made the mistake of also adding a reference to this Person class in my web.config, like this: , but I got an error. I discovered that you can either define the property in your custom ProfileBase class, or you can define it in the web.config, but you cannot put it in both places, or you can get an error saying the property is duplicated.)

I know thought that I had everything wired up to work. Unfortunately, though, whenever I tried to access the HttpContext.Current.Profile property in my code, I kept getting a null value. I spent several hours beating my head against the wall until I read this posting which says you need to add the Profile module back to the web.config, because apparently SharePoint strips it out:

<httpModules>
  <add name="Profile" type="System.Web.Profile.ProfileModule" />
</httpModules>

Finally! The code was working. I wanted to be able to access a given user’s profile information so I could retrieve it or save it in the “UserEdit” page in the CKS code. My code now looks like this:

UserProfile profile = UserProfile.GetProfile(user.UserName);
Person person = profile.Person;
person.Fax = txtFax.Text;
person.Phone = txtPhone.Text;
profile.Save();

The “GetProfile” method is referencing the static method we added to the UserProfile class, which is simply generating a new Profile object that we can access for a particular user. I could just as easily access the UserProfile object for the currently-logged-in user if I were to use code like this:

UserProfile profile = (UserProfile)HttpContext.Current.Profile;

Here are two blog articles that I found particularly helpful when looking up how to write custom profile code:
http://weblogs.asp.net/jgalloway/archive/2008/01/19/writing-a-custom-asp-net-profile-class.aspx
http://odetocode.com/Articles/440.aspx