Friday, May 11, 2012

Implementing Custom Profile in ASP.NET MVC 4 using ProfileBase


This article aims to explain how to work with ProfileProvider in ASP.NET MVC 4, and implementing custom user profile.
As we know, the membership, roles and profile tables have been restructured in MVC 4. Profile information of each user now gets stored in “Profile” table in database.
The below screenshot is taken from an SQL Server database, but you can easily correlate it with other database if your data provider is Oracle, etc. Also, it will work if your profile table is pre-MVC 4 version (i.e. aspnet_Profile).


Each row in this table represents a User profile associated with a User (UserId links User with the profile). 

For each user there is only one row in this table containing complete information about profile.
PropertyNames >> Contains name of each property in profile
PropertyValueStrings >> Text value of each property of corresponding user’s profile. Its stored based on character index. Each profile will have more than one property (as we will see later), and character indexing helps in identifying values corresponding to property.
PropertyValueBinary >> Binary representation of PropertyValueStrings field.

As such, we are least concerned about how the values are stored and retrieved in database, because it’s a job of Profile provider in ASP.NET MVC 4. We do not need to directly deal with this table at all!
Let us have a Walkthrough to understand how to implement this:

If you are not aware about how to enable membership, role and profile providers (System.Web.Providers) in your MVC 4 application, please do visit this link before looking into below walkthrough.

STEP 1 Configure Profile provider in web.config:
Make sure you have following section in <system.web> in your web.config file. Here, “DefaultConnection” is a ConnectionString's name in <ConnectionStrings> section of your web.config. 
Change name, connectionStringName, and applicationName attributes according to your preferences.

<profile defaultProvider="DefaultProfileProvider">

      <providers>
        <add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="MyApp"/>       
      </providers>
</profile>

STEP 2 Define Profile properties:
Define properties that constitutes a user profile in your application. Add them under above added <profiles> section in your web.config. Profile properties can also be grouped, such as Contact, Address, etc.

<properties>
<add name="ForeNames" type="System.String"/>
<add name="LastName" type="System.String"/>
<add name="Gender" type="System.String"/>
<group name="Contact">
<add name="MobileNo" type="System.String"/>
<add name="EmailAddress" type="System.String"/>
</group>
</properties>

That’s all you need to do in web.config.

STEP 3 Add Model for Profile object in your MVC application:
This model represents Profile in your MVC application. Ideally, it should be in sync with the one defined in Web.config.
This model will be used to pass Profile data from Controller action to View, and post them back from View to Controller action.
Add following model in “Models” folder in your MVC structure. (Make a new file, or add it in existing file. It’s all up to you.)
public class ProfileModel
{
[Display(Name = "First Name")]
        public string ForeNames { get; set; }

        [Display(Name = "Last Name")]
        public string LastName { get; set; }

        [Display(Name = "Gender")]
        public string Gender { get; set; }

        [Display(Name = "Mobile No")]
        public string MobileNo { get; set; }

        [Display(Name = "Email Address")]
        public string EmailAddress { get; set; }
}

STEP 4 Add a new View, and Bind it with ProfileModel:
Add a new view under “Views” folder (Place it under a controller-specific view folder, or a “Shared” folder, wherever you wish to).
Bind the view with the ProfileModel, and design UI accordingly, and also enforce validations as you wish.
UI should include entry fields for corresponding profile properties, as well as showing validation messages, and a Submit button to the corresponding POST action in controller. (Next steps will show you controller actions).
I am leaving the designing of view to you!

Sample:



STEP 5 Write a GET action to fetch user profile using Profile provider, and launch View created in above step:
A GET action needs to be written in your controller to fetch profile information from database using Profile provider.

public ActionResult EditProfile()
{           
      ProfileBase _userProfile = ProfileBase.Create(HttpContext.User.Identity.Name);
      ProfileModel _profile = new ProfileModel();
      if (_userProfile.LastUpdatedDate > DateTime.MinValue)
      {
          _profile.ForeNames = Convert.ToString(_userProfile.GetPropertyValue("ForeNames"));
          _profile.LastName = Convert.ToString(_userProfile.GetPropertyValue("LastName"));
          _profile.Gender = Convert.ToString(_userProfile.GetPropertyValue("Gender"));
          _profile.MobileNo = Convert.ToString(_userProfile.GetProfileGroup("Contact").GetPropertyValue("MobileNo"));
          _profile.EmailAddress = Convert.ToString(_userProfile.GetProfileGroup("Contact").GetPropertyValue("EmailAddress"));
       }
       return View(_profile);
}

The above controller action is HTTP GET method, and should be called from “Edit Profile” or similar link of your choice in your application. (I have given an example in STEP 7 later)
First of all, it retrieves profile information of specific user (passed as parameter in ProfileBase.Create method) from database.
Also, ProfileModel is instantiated; this is what we are binding with the “Edit Profile” view.
Further, if there exists a user profile for the user, then properties of ProfileModel instance gets assigned according to the actual values in user profile.
Finally, a View is invoked by supplying ProfileModel object for binding.
Note: Here, I assume that your View is named as “EditProfile” and exists in the same controller.
If that’s not the case, you need to set appropriate parameters in a call to “View” method.

STEP 6 Write a POST action to post model to View:
When a user clicks on a Submit button in “Edit Profile” view, an HTTP Post action from a controller gets called. This method accepts a ProfileModel instance updated by user through “Edit Profile” view.

[HttpPost]
public ActionResult EditProfile(ProfileModel model)
{
    if (ModelState.IsValid)
    {
       // Attempt to register the user profile
       System.Web.Profile.ProfileBase profile = System.Web.Profile.ProfileBase.Create(User.Identity.Name, true);

       if (profile != null)
       {
           profile.SetPropertyValue("Gender", model.Gender);
           profile.SetPropertyValue("ForeNames", model.ForeNames);
           profile.SetPropertyValue("LastName", model.LastName);
           profile.GetProfileGroup("Contact").SetPropertyValue("MobileNo", model.MobileNo);
           profile.GetProfileGroup("Contact").SetPropertyValue("EmailAddress", model.EmailAddress);
           profile.Save();
       }
       else
       {
           ModelState.AddModelError("", "Error writing to Profile");
       }
    }     
    return View(model);
}

The above controller action is an HTTP POST method, and gets called when a user clicks a Submit button to Save profile changes.
It first validates, if the user profile changes satisfies all the validations. If not, it will create an instance of ProfileBase object using Reflector for the specific user. It will assign profile properties from the values updated in ProfileModel object. Once done, Save user profile in database. (If the profile for the particular user does not exist, it will create a new one, or else, it will update existing profile).

Step:7 Call to GET action
Finally, we need a user interface to allow user to launch “Edit Profile” view. And for that, we need to call “EditProfile” HTTP GET action (Step 5) of the controller from some UI element.
@Html.ActionLink("Edit Profile", "EditProfile", "Account")

Our example is specific to deal with logged in user’s profile only. But you can easily tweak it by adding a parameter to accept user name to allow users to view/ manage profile of other users’ in your application.

12 comments:

  1. Thank you, thank you so much !!!
    I'm new to MVC and I was searching for some simple profile provider code for about 2 days now. I'm so glad that I found this article finally. It's so simple, and super clear, I love when I can just cut-paste code in my project and see it WORKING just fine. Everybody out there use some DBContext, that I couldn't implement for days. Hate when programmers complicate their code so much.

    Thank you again for such a great article !!!

    ReplyDelete
    Replies
    1. thanks Silvia for appreciating the efforts, and motivating words..

      Hopefully, I will be providing some more MVC posts in near future.

      Keep Visiting :)

      thanks

      Delete
  2. Thanks Nirman Doshi,

    I really appricate your hard work and saving my time to get this done in minutes

    -Thanks
    Peter

    ReplyDelete
  3. Hi,Ikeep getting the following error.please advise.
    "The parameter 'usernameToMatch' must not be empty.
    Parameter name: usernameToMatch"
    in the following line of code
    "if (_userProfile.LastUpdatedDate > DateTime.MinValue)"

    ReplyDelete
  4. Wonderful share Nirman, Thanks a lot for sharing !!

    ReplyDelete
  5. It was really a nice post and I was really impressed by reading this .NET Online Course Bangalore

    ReplyDelete
  6. If you want to take a great deal from this post then you
    have to apply these strategies to your won blog.

    ReplyDelete
  7. keep up the good work. this is an Assam post. this to helpful, i have reading here all post. i am impressed. thank you. this is our digital marketing training center. This is an online certificate course


    Dot Net Training in Chennai | Dot Net Training in anna nagar | Dot Net Training in omr | Dot Net Training in porur | Dot Net Training in tambaram | Dot Net Training in velachery

    ReplyDelete

Thanks for visiting my blog.
However, if this helped you in any way, please take a moment to write a comment.

Thanks
Nirman