Using Custom Data Types in ASP.NET Profiles

ASP.NET Profile can also accept custom data types and they are relatively easy to implement

The first step is to create a class which wraps the information you require. In the class, you may use public member variables, but the preferred choice is full-fledged property procedures which will allow the  class to support data binding or other complex logic.

For example, the below code shows an  Address class which should be placed in the App_Code directory of the web app:

[Serializable()]
public class Address
{
private string fullName;
public string FullName {...}
private string streetNumber;
public string StreetNumber {...}
private string cityCode;
public string CityCode {...}
private string zip;
public string Zip  {...}
private string stateCode;
public string StateCode {...}
private string countryCode;
public string CountryCode {...}
public Address(string nameCode, string streetCode, string cityCode,
string zip, string stateCode, string countryCode)
{
NameCode = nameCode;
StreetCode = streeCodet;
CityCode = cityCode;
Zip= zip;
StateCode = stateCode;
CountryCode = countrCode;
}
public Address()
{ }
}

Next add a property in the web.config to declare it:

<properties>
<add name="CutomerAddress" type="Address" />
</properties>

Now you can use the Profile in your code.


To assign values to the Profile:

Profile.CutomerAddress.Zip = txtZip.Text;

To access the Profile data:

string zipStr;
zipStr = Profile.CutomerAddress.Zip;

Automatic Saves

The ASP.NET Profiles feature cannot detect changes in complex data types (ie anything other than strings, Boolean values, simple numeric types etc). So the Profile includes complex data types, ASP.NET will save the complete profile info at the end of every request which accesses the Profile. The behavior has an obvious performance cost. Therefore to optimize Profile performance when using  complex types, you can  set the profile property to be read-only (in the event it never changes).

Alternatively, you can disable the autosave behavior by using  the automaticSaveEnabled attribute in the <profile> element and setting this to  false. If you do this you will need to use Profile.Save() to explicitly save changes to the Profile. This approach is normally preferred as the parts of code which modify a Profile are easy to spot and you can easily add  Profile.Save() to the end of the code block:

Profile.CustomerAddress = new Address(txtName1.Text, txtStreet1.Text, txtCity1.Text,

txtZip1.Text, txtState1.Text, txtCountry1.Text);

Profile.Save();

Optimizing ASP.NET Profiles Performance

ASP.NET Profiles were introduced to assist developers in persisting user information. Previous methods of persistence all had limitations in how they stored user data, Session state would only be held in memory and lost once the user’s session ended, a query-string would only be useful for that particular page and had to be recreated on each new page, cookies are only available on a single user machine. Profiles addressed all these difficulties by providing a simple persistent store which plugs into ASP.NET Membership. Profiles are ideal for storing user info such as preferences for a web app, besides being convenient they are very simple to use – just create them in the web.config file and access them anywhere in the application using Profile.ProfileName.

But with the convenience and power of Profiles comes a price – performance. Profiles are stored in a database, and therefore if used without caution can have a major performance cost.

To understand how best to use Profiles, first we will look at how they work under the hood. Profiles plug into the life-cycle of the page at two points:

  • The first time the Profile object is accessed in your code ASP.NET retrieves all the  profile data for the current user from the   database. If   the profile data is used more  than once in the same request ASP.NET reads it only once and then reuses it.
  • If profile data is updated, that update is deferred until the page has finished processing( ie after the PreRender, PreRenderComplete, and Unload events have completed). At that point the profile data  is written   to the database, thus  multiple changes are updated in batch.

Thus, using Profiles can result in an extra two database hits  per  request (if Profile data is read and then updated) or one extra database hit  (for simply reading the Profile data). It should be noted that Profiles do not have a caching mechanism so so every request for Profile data or update of Profile data  requires a database connection.

Thus from a performance viewpoint, Profiles are best when:

  • There are a relatively small number of pages which access the Profile data.
  • Profiles only store small amounts of data (since accessing Profiles always results in the retrieval of all the Profile data for that user it can be quite result in large payloads).

Therefore to optimize performance when using ASP.NET Profiles it is best to combine  

Profiles with other methods of  state management. For example,   a web app could first check if there was a cookie stored on the user’s machine for the user’s date format preference and if not available  this data could be retrieved from the Profile (which would then then add a cookie) this will save a database round trip each time to check the preferences (session state could also be used for this).

Getting Started Using ASP.NET Profiles

ASP.NET Profiles are a very useful tool for  persisting user data. Most other methods of state management do not easily persist the data across user visits, but Profiles plug seamlessly into the ASP.NET Membership database to provide a convenient persistent store.

Defining Profile Properties

The first step to using Profiles is to defining them in the web.config file.  This is done by adding the <profile> section to the web.config file and the adding each property using a  <add> element nested inside the <properties> element:

<configuration>
<system.web>
...
<profile>
<properties>
<add name="Language"/>
<add name="NumberFormat"/>
<add name="JoinedDate"/>
</properties>
</profile>
</system.web>
...
</configuration>

In addition to name the <add> element accepts several attributes which should be used. By default the format of the Profile is set to String but can be set to any datatype, for example the above JoinedDate profile should have a attribute of type added with the associated data type:

<add name="JoinedDate" type="System.DateTime" />

defaultValue is another useful attribute which sets the default of the Profile. For example, this could be used the set the initial language a user’s preferences is set to:

<add name="Language" defaultValue="en" />

There are several additional attributes, namely:

  • serializeAs : The format to use for serializing this Profile (String, CML, Binary, or ProviderSpecific)
  • readOnly : This is a boolean which sets if the Profile can be updated.
  • allowAnonymous : A boolean which sets if the Profile can be used with anonymous profiles.
  • provider : The profile provider that is used to manage this property.

Access Profiles

Profile access is very simple. Just use Profile.ProfileName anywhere in an app to get the profile value for the user. For example:

String langStr = Profile.Language

Update Profiles

Updating ASP.NET  Profiles is also a simple procedure, just assign the value to the Profile and it will be stored:

Profile.Language  = langTxtBox.Text

Note that the Profile will not actually be written to the database (and therefore not stored) until the page life-cycle is complete. Therefore after updating a Profile, avoid accessing it unless the page has finished processing (as only the old value will be stored).

Be aware that Profiles do not come without issues. There is a performance cost to using Profiles inappropriately see ASP.NET Profile Performance for more details.