Monday, March 8, 2010

Extending the ProfileBase class in ASP.NET

In this blog post I'll show you how you can extend the ProfileBase class in ASP.NET. This is useful in many situations, for example if you need to create a custom shopping cart. However, as a shopping cart can be quite complex, I've chosen to use a simpler example in this case.

Assume you have a website containing a large number of news articles and you want to let your authenticated users add personal comments to the each of the articles. By personal comments I mean comments that are stored in a user's profile and are therefore only accessible to the user who created the comment.

Our first step is to create a Comment class. Note that the Comment class has to be serializable.

    [Serializable]
public class Comment
{
private readonly int _articleId;
private readonly string _commentText;
private readonly DateTime _created;

public Comment(int articleId, string commentText)
{
_articleId = articleId;
_commentText = commentText;
_created = DateTime.Now;
}

public string CommentText
{
get { return _commentText; }
}

public int ArticleId
{
get { return _articleId; }
}

public DateTime Created
{
get { return _created; }
}
}



The second class we need is a CommentCollection class. This class contains a list of a specific users comments, as well as methods for adding, removing and editing comments. To keep it short, I’ve only included the InsertComment() method in this blogpost.



    [Serializable]
public class CommentsCollection
{
private readonly List<Comment> _comments;

public CommentsCollection()
{
if (_comments == null)
{
_comments = new List<Comment>();
}
}

public Collection<Comment> Comments
{
get
{
return new Collection<Comment>(_comments);
}
}

public void InsertComment(int articleId, string commentText)
{
Comment comment = new Comment(articleId, commentText);
_comments.Add(comment);
}
}


So now we have a class for managing the comments, but how do we use this? First of all, we have to register it as a property in the <system.web><profile><properties> section of the web.config:



<add name="CustomComments" type="Your.Namespace.CommentsCollection" serializeAs="Binary" />



Now it’s time to create a CustomProfile class, extending the Profilebase class. This class contains two methods. One for retrieving the Profile of the currently logged-in user, and one for retrieving the Profile of a specific user.



    public class CustomProfile : ProfileBase
{
/// <summary>
/// Get the profile of the currently logged-on user.
/// </summary>
public static CustomProfile GetProfile()
{
return (CustomProfile)HttpContext.Current.Profile;
}

/// <summary>
/// Gets the profile of a specific user.
/// </summary>
public static CustomProfile GetProfile(string userName)
{
return (CustomProfile)Create(userName);
}

/// <summary>
/// Gets a users comment collection
/// </summary>
public CommentsCollection CommentsCollection
{
get { return (CommentsCollection)GetPropertyValue("CustomComments"); }
}
}


We also have a property called CommentCollection that uses ProfileBase.GetPropertyValue to get the value of the CustomComments property registered in the web.config. Additionally, in order for ASP.NET to use your custom class instead of the ProfileBase class, this has to be specified in the web.config:



<profile enabled="true" defaultProvider="SqlProfile" automaticSaveEnabled="true" inherits="Your.Namespace.CustomProfile">



And that’s it! Here’s an example of how to use CustomProfile and CommentCollection to create comments:



     //Get the profile of the currently logged-in user
CustomProfile.CustomProfile profile = CustomProfile.CustomProfile.GetProfile();

// Get the users comment collection
CommentsCollection commentsCollection = profile.CommentsCollection;

// Insert new comment
commentsCollection.InsertComment(1, "My first comment");

// Get a users comments
Collection<Comment> comments = commentsCollection.Comments;


Note: There seems to be a bug in ASP.NET, so if you get the error “Unable to case object of type ‘ProfileCommon’ to type ‘CustomProfile’”, change <profile enabled="true">

to <profile enabled="false"> and back again to true. After this everything will work fine. If you find the cause of this bug, please let me know :)