Writing a custom ASP.NET Profile class

We made heavy use of the ASP.NET membership and profile system for Video.Show (a Silverlight 1.0 video community website system, available on CodePlex). In addition to storing basic profile information, we created a custom profile with some additional fields. It's a really easy way to add add some additional personalization to your site without having to add a bunch of tables to your database.

This is really simple if you're using a Website Project - you can just add additional properties to the profile section of your web.config, and a custom profile class is generated on the fly when you rebuild your application. That makes things ridiculously easy. First, we'd define the property in web.config:

<!-- In web.config -->
<profile >
  <properties>
    <add name="FavoritePasta" />
  </properties>
</profile>
Then you can refer to the Profile.FavoritePasta profile setting anywhere in your web application, and it's automatically mapped to the current user:
Profile.FavoritePasta = "Pumpkin Ravioli";
And you can access the data just as you would a session property:
<span id="user-favorite-pasta"><%= Profile.FavoritePasta %></span>

Not so fast, I'm using a Web Application Project

Yeah, here's the catch. If you're using the Web Application Project model, the custom build handling for the profile doesn't kick in, so those custom properties you've lovingly crafted in your web.config aren't going to be compiled into a custom profile class.

There's a Visual Studio 2005 add-in called WebProfile that reads your custom profile and creates a custom class for you. That's handy, but I passed on it. For one thing, I haven't heard that there's a VS 2008 version of this. Additionally, I don't like to require a custom add-in in order to get my code to work in case I want to add a new profile property - especially when I'm working on a project that's going to be distributed on CodePlex.

Fortunately, it's not very hard to implement a custom profile. First, we'll write a class that inherits from System.Web.Profile.ProfileBase. I added a few static accessors, too:

using System.Web.Profile;
using System.Web.Security;

namespace VideoShow
{
    public class UserProfile : ProfileBase
    {
        public static UserProfile GetUserProfile(string username)
        {
            return Create(username) as UserProfile;
        }

public static UserProfile GetUserProfile() { return Create(Membership.GetUser().UserName) as UserProfile; } [SettingsAllowAnonymous(false)] public string Description { get { return base["Description"] as string; } set { base["Description"] = value; } } [SettingsAllowAnonymous(false)] public string Location { get { return base["Location"] as string; } set { base["Location"] = value; } } [SettingsAllowAnonymous(false)] public string FavoriteMovie { get { return base["FavoriteMovie"] as string; } set { base["FavoriteMovie"] = value; } } } }

 
Now we need to  hook that up in the profile section of web.config - notice that I've included inherits="VideoShow.UserProfile" in the profile declaration:
 
<profile inherits="VideoShow.UserProfile">
  <providers>
    <clear />
    <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="VideoShowConnectionString"/>
  </providers>
</profile>
 
With that done, I can grab an instance of the custom profile class and set a property:
 
//Write to a user profile from a textbox value
UserProfile profile = UserProfile.GetUserProfile(currentUser.UserName);
profile.FavoriteMovie = FavoriteMovie.Text;
profile.Save();

Part of the reason for the accessor is to allow display of profile information for users other than the current user - for instance, a public profile page which displays information about other users in the system.

//Write to a user profile from a textbox value
UserProfile profile = UserProfile.GetUserProfile(displayUser.UserName);
Response.Write(profile.FavoriteMovie)

And of course, I can still databind to it as well:

<span id="user-favorite-movie"><%= VideoShow.UserProfile.GetUserProfile().FavoriteMovie %></span>

A few disclaimers:

  • This isn't news, it's been out since ASP.NET 2.0 shipped. Still, it's pretty handy to know about, and if you're like me you may have forgotten or never really dug into some of the ASP.NET 2.0 goodies.
  • This isn't the ultimate solution in terms of entity modeling. Custom profile information is stored in two columns in the aspnet_Profile table (delimited strings in one column, another column for binary serialized objects). That means that the only real way to read or write custom property values is via the profile API. That's not a real problem unless you need to query or join on information stored in a custom profile setting.

Further information:

Profiles in ASP.NET (K. Scott Allen)

Essential ASP.NET 2.0, Chapter 5 (Fritz Onion)

53 Comments

  • Thanks for this. Saved me a lot of pain trying to figure out why it wouldn't work in my Web Application Project. Hopefully I'll get it going in a web service I'm putting together too.

  • And here's a tip that may save you another hour of pain ;)

    Make sure that the name of the property you create matches the base property you try to return i.e. don't do this:

    [SettingsAllowAnonymous(false)]
    public string UserAge
    {
    get { return base["Age"] as string; }
    set { base["Age"] = value; }
    }

    Otherwise it will complain with
    "The settings property 'Age' was not found."

    Change 'Age' to 'UserAge' and it will work.

  • Can someone tell me how to translate the first two functions to VB.NET ? would be great.

    Thanks!

  • Thanks for this code Jon.

    I've implemented it easily in my site and it works great, but have you been able to unit test it? On my first attempt, the inherited "Create" method returned a DefaultProfile object, which caused a casting failure. I figured out that the Unit Tests were not using the web.config file to see the custom Profile Provider I configured, then I figured out you need to copy the web.config to the unit test project, and rename it to app.config. However now I get the error.

    "Test method TestMyProject.UserControllerTest.CreateNewUserTest threw exception: System.TypeLoadException: Could not load type 'MyRootNamespace.Profile.UserProfile' from assembly 'System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.."

    I don't know why it is not looking the DLL that contains my custom "UserProfile" class. Any ideas? It works in my website, but not in the Unit Test. The UnitTest project references the project containing the Profile class. I'm using VS2008 built-in Unit Testing.

    Thanks in advance.

  • Anyone have any ideas about Scott's post above?

    I am also trying to unit test the custom profile class. Any ideas?

    Thanks,
    Tim

  • hei .... can anyone please help me ?

    I am trying to implement the custom profile class... so far so good... it just doesn't retrieve any info at all :(

    i have added this in my web.config










    and my UserProfile class is very much like the one above....

    did anyone else experience this problem ?

    thanks in advance ...
    andu

  • Thanks for saving me a ton of time!

    Great article!


    Best,

    Brett

  • How would you handle groupings using this method?

    Thanks!

    Brett

  • I am facing the same issue as Scott and Tim above. I've posted about it on the forums:

    http://forums.asp.net/p/1270044/2397754.aspx#2397754

  • I echo Brett's question. How in the heck do you handle grouping properties? I've tried Googling to no avail.

  • So will it automatically grab the related value from the profile table or a class is needed to do that job? I am little confused

  • So will it automatically grab the related value from the profile table or a class is needed to do that job? I am little confused.

  • UserProfile profile = UserProfile.GetUserProfile(displayUser.UserName);
    Giving null object.
    how to resove it?

  • I'm Getting this problem on a Web Application. It's running on the website. Any Ideas?

  • Thanks Martin Bayly for CustomProviderData howto use mention!

  • Thx for your explanation. It helped me a lot.

  • Awesome! Thanks for this code Jon.
    I've implemented it easily in my site and it works great. Thanks for saving me a ton of time. It helped me a lot.

  • Is it possible to set a property as "[SettingsAllowAnonymous(true)]" and get the profile for an anonymous user?

    I have been trying to do this but I am not able.

    Could someone, please, help me out?

  • How in the heck do you handle grouping properties in the class? Was anyone able to find the answer, if so could you please post it.

  • I was able to answer my own question. What I did was i used a demo website project and setup my profile properties and groups in the webconfig so that it would autogenerate the class into Asp.Net Temporary files and then looked at how the code got generated for the groups.

  • Did anyone figure out how to get a custom profile class with groups to work with the SqlStoredProcedureProfileProvider? I can't get it to pick up my groups

  • DRS, can you post an example of the group code?

  • I'm also unable to get profile groups to work with the custom profile class. Has anyone played around with attributes of the tag to see if there's some "secret" setting that causes a ProfileGroup to get recognized by ASP.NET and included in the custom object that it generates?

  • Can this profile type be accessed for a winform, is so how?

  • Thanks, Jon. You just saved my sanity.


  • UserProfile profile = UserProfile.GetUserProfile(displayUser.UserName);

    Giving null object. because Unable to cast object of type 'ProfileCommon' to type 'UserProfile'.

    how to resove it?

    thanks

  • Has anyone developed a solutin to the null object problem that several people asked about? I implemented this custome profile provider exactly as shown but keep getting a null profile back. The UserProfile.Save() does not appear to be saving at all. Any help?

  • If you want to retreive values from your DB you have to specify defaultProvider in profile section of your config file. When this is the case you will have user profile data in object.
    Cheers

  • After looking for days to figure out why i was getting null objects, why I was getting identical assemblies, and why this simple setup would not work I have the answer.

    1. for identical assembly errors remove your global application class if you have one. I have no clue why on earth this would cause an error but it does.

    2. casting (UserProfile)Create(username) will not run if the casting operation doesn't work. and the Create(UserName) as (UserProfile) will return a null if the casting operation doesn't work. Because you should be able to perform a cast at this point you should not get any errors.

    The problem is because your web pages are still maintained as a web project not a web application project. This formality has the undoe consequence of creating a ProfileCommon class then trying to cast to it. This creates an invalid cast or null whatever have you. Simply right click your project and tell it to convert to web application. The system will no longer build a profileCommon class and you should be able to now cast the profilebase to a userprofile type.

    It's amazing how the tiny things always slip by.

  • I'm banging my head against the wall here. I created the class for the profile and it works fine in my Web App. But I have a compiled DLL that I want to access the profile data as well and all I can get is "Object reference not set to an instance of an object." when I try to access the profile.

    The compiled DLL can access CONNECTIONSTRINGS and APPSETTINGS from the web.config but not the profile.

    What am I missing?

  • Following Thomas's advice I have got it to work only with one minor alternation - It converts my App_Code folder into Old_App_Code - but on doing so it works. Any solutions on how to get this to leave App_Code as App_Code?

  • Hi, i'm using this property

    [SettingsAllowAnonymous(false)]
    public Dictionnary ClientLayoutData
    {
    get { return base["ClientLayoutData"] as string; }
    set { base["ClientLayoutData"] = value; }
    }


    when i save the profile, i'm getting ... is not supported because it impelemts idictionary.

    help ?

  • Isn't
    Dictionnary != string
    the problem?

  • A fantastic post that saved me some time, just wanted to add, that if you are unit testing or using a custom profile class in console applications and the class lives in a separate assembly, say a code library project in the same solution. make sure that you tell it the partial assembly name with means the profile element in the config file changes to:

    profile inherits="namespace.namespace.className, assemblyName"

    other wise the app freaks out and says it can't find the class in assembly System.Web or something nonsensical like that

  • I keep getting the following error when running the application:

    CS0234: The type or namespace name 'UserProfile' does not exist in the namespace 'AgileApp' (are you missing an assembly reference?)

    I have the following in my web.config file:



    The namespace in the UserProfile class is set to AgileApp. Am I missing something?

  • ok so i have my profiles working and i've even added a photo. so how do i allow users to creat their own page and display it for people like myspace or other community sites? is this called serialization or something like that? thanks for your help!

    dvdgzzrll@msn.com

  • You saved my day. (Maybe I lacked some search skills here, but I just could not find proper help on this one)
    Implemented in the Krok Asp.Net MVC Template Project.

  • to resolve this error

    Unable to cast object of type 'System.Web.Profile.DefaultProfile' to type
    MyNameSpace.AccountProfile

    1. Right click on project, Convert to Web Application

    2. In your web.config, make sure you use the "inherits" attribute to use your custom class








  • Readers may also be interested in this solution:

    Using the SQL Table Profile Provider in ASP.NET 4 Web Applications (C# & VB)

    http://weblogs.asp.net/kencox/archive/2010/09/05/using-the-sql-table-profile-provider-in-asp-net-4-web-applications-c-amp-vb.aspx

  • Thanks! After a day of searching, this was the first article I found with a workable solution on using the Profile class in a Web Application Project. I followed your instructions and it works, even on my GoDaddy hosted asp.net site.

  • This seems to work for simple Strings, but I can't get it to work for complex types. I am trying to persist a shopping cart, to a the database, but it's not working as expected

  • Heya,

    I figured it out. I was using an IList, and the compiler was having trouble serializing it. Changed it to a List and all is good.

    Ross

  • Hi All,

    Firstly, a HUGE thanks to Jon (and other contributors) on helping with creating the custom Profile. This was a problem that I encountered while migrating our ASP.Net web site to Azure platform, which I need to convert into a web application.

    Anyway, I had a problem where I would create a UserProfile instance by:

    UserProfile profile = UserProfile.GetUserProfile();

    This would always create me a new ProfileCommon, so it does not keep the profile properties whenever I set or get it. I have managed to resolve this by casting the HttpContext.Current.Profile instead, and that works a treat.

    e.g. ((UserProfile)HttpContext.Current.Profile).

    Keep up the good work, brilliant article.

    Many Thanks - Jay

  • Thank You!!!

    I've been trying to find a clear explanation of how to implement this for hours.

    This works like a charm!

  • How does ProfileCommon get created in the web site projects ? Does it require a code generator to add the properties to a generated module?
    Or can you just use the Create method and get a profile with the web.config properties recognized ? ? I think the properties are in a collection in this class.

    If you add properties to your own custom class...do the inherited methods from PofileBase work ? Save? GetProperty? SetProperty?

  • UserProfile CLASS:

    Namespace MIDASProfileManager
    Public Class UserProfile
    Inherits ProfileBase

    Public Shared Function GetUserProfile(username As String) As UserProfile
    Return TryCast(Create(username), UserProfile)
    End Function

    Public Shared Function GetUserProfile() As UserProfile
    Return TryCast(Create(Membership.GetUser().UserName), UserProfile)
    End Function

    _
    Public Property Theme() As System.String
    Get
    Return DirectCast(MyBase.Item("Theme"), String)
    End Get
    Set(value As System.String)
    MyBase.Item("Theme") = value
    End Set
    End Property

    _
    Public Property ThemeSelectedIndex() As Integer
    Get
    Return DirectCast(MyBase.Item("ThemeSelectedIndex"), Integer)
    End Get
    Set(value As Integer)
    MyBase.Item("ThemeSelectedIndex") = value
    End Set
    End Property

    End Class
    End Namespace

    Web.Config:









    In Global.asax Session_Start:

    Dim Profile As UserProfile = UserProfile.GetUserProfile(SV.Username)
    SV.ddTheme = Profile.Theme
    SV.ddThemeSelectedIndex = Profile.ThemeSelectedIndex

    My SV Class:

    Imports MIDAS.MIDASProfileManager

    '''
    ''' Session variables class - provides a facade to the ASP.Net Session Object.
    ''' All access to Session variables must be through this class.
    ''' Create an instance, populate and store in Session object for each session.
    '''
    Public NotInheritable Class SV

    Public Shared ReadOnly Property Username As String
    Get
    Return HttpContext.Current.User.Identity.Name
    End Get
    End Property

    Public Shared Property ddTheme As String
    Get
    If HttpContext.Current.Session("ddTheme") Is Nothing Then
    Return GV.DefaultTheme
    Else
    Return CStr(HttpContext.Current.Session("ddTheme"))
    End If
    End Get
    Set(ByVal value As String)
    If CStr(HttpContext.Current.Session("ddTheme")) = value Then
    Return
    End If
    ' TODO: To change to also store in profile
    HttpContext.Current.Session("ddTheme") = value
    Dim Profile As UserProfile = UserProfile.GetUserProfile(SV.Username)
    Profile.Theme = value
    Profile.Save()
    End Set
    End Property

    Public Shared Property ddThemeSelectedIndex As Integer
    Get
    If HttpContext.Current.Session("ddThemeSelectedIndex") Is Nothing Then
    Return GV.DefaultThemeSelectedIndex
    Else
    Return CInt(HttpContext.Current.Session("ddThemeSelectedIndex"))
    End If
    End Get
    Set(ByVal value As Integer)
    If CInt(HttpContext.Current.Session("ddThemeSelectedIndex")) = value Then
    Return
    End If
    ' TODO: To change to also store in profile
    HttpContext.Current.Session("ddThemeSelectedIndex") = value
    Dim Profile As UserProfile = UserProfile.GetUserProfile(SV.Username)
    Profile.ThemeSelectedIndex = value
    Profile.Save()
    End Set
    End Property

    End Class

    My GV Class:


    Imports System.Data
    Imports System.Linq
    Imports System.Web
    Imports Microsoft.Practices.EnterpriseLibrary.Logging

    '''
    ''' Contains my site's global variables.
    '''
    Public NotInheritable Class GV

    Private Sub New()
    End Sub

    Public Shared Property DefaultTheme() As String
    Public Shared Property DefaultThemeSelectedIndex() As Integer

    End Class

    Global.asax Application_Start has:


    GV.DefaultTheme = System.Configuration.ConfigurationManager.AppSettings("DefaultTheme")
    GV.DefaultThemeSelectedIndex = CInt(System.Configuration.ConfigurationManager.AppSettings("DefaultThemeSelectedIndex"))

  • Ive put your userprofile class in the App_Code Folder of my web app project + the inherits code into web.config.

    I still cant access the user profiles class in any of my code behind files.

  • Jon,
    How would one add a Profile property to a basepage based on your UserProfile class having functionality for both getprofile() and getprofile(username)? Thnx in advance, a.

  • this works file in local, if hosted it in server it showing error.


    ERROR: The type or namespace name 'Admin' could not be found (are you missing a using directive or an assembly reference?)

    this works fine in :www.mywebsite.com, showing error in www.mywebsite.com/admin.

    i have subfolder called admin and i'm using this userinfo class in main directory,if access it like /admin it showing above error.
    I'm working with mvc3 in main directory and webpage(aspx) in /admin folder.

  • this works file in local, if hosted it in server it showing error.


    ERROR: The type or namespace name 'Admin' could not be found (are you missing a using directive or an assembly reference?)

    this works fine in :www.mywebsite.com, showing error in www.mywebsite.com/admin.

    i have subfolder called admin and i'm using this userinfo class in main directory,if access it like /admin it showing above error.
    I'm working with mvc3 in main directory and webpage(aspx) in /admin folder.

  • Perfact code. specially web.config entries.

  • Running VS2012 with ASP.NET 4.5 with Web Forms Application I ran into the same problem (still..)

    You saved my day!

  • Just a tip for anyone else who runs into this problem, I ran into pretty much the same thing Andu described: I was able to implement everything fine, but when I'd do a lookup on a custom property, it would always return an empty string. Turns out I was missing the attribute [CustomProviderData("propertyName;SqlDbType")] for each field in the Profile subclass. After that, worked like a champ.

    This is an older blog post, but really helped me in porting over an old app, thanks, much appreciated.

Comments have been disabled for this content.