Examining ASP.NET's Membership, Roles, and Profile - Part 6
By Scott Mitchell
A Multipart Series on ASP.NET's Membership, Roles, and Profile |
---|
This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.
SqlMembershipProvider and the security Web controls.aspnet_regsql.exe ).SqlProfileProvider . |
Introduction
The Membership API in the .NET Framework provides the concept of a user account and associates with it core properties: username, password, email, security question and answer, whether or not the account has been approved, whether or not the user is locked out of the system, and so on. However, depending on the application's needs, chances are your application needs to store additional, user-specific fields. For example, an online messageboard site might want to also allow users to specify a signature, their homepage URL, and their IM address.
There are two ways to associate additional information with user accounts when using the Membership model. The first - which
affords the greatest flexibility, but requires the most upfront effort - is to create a custom data store for this information.
If you are using the SqlMembershipProvider, this would mean creating an additional database table that had as a primary key
the UserId
value from the aspnet_Users
table and columns for each of the additional user properties.
In the online messageboard example, the table might be called forums_UserProfile
and have columns like
UserId
(a primary key and a foreign key back to aspnet_Users.UserId
), HomepageUrl
,
Signature
, and IMAddress
.
Rather than using custom data stores, the ASP.NET Profile system can be used to store user-specific information.
The Profile system allows the page developer to define the properties she wants to associate with each user. Once defined,
the developer can programmatically read from and assign values to these properties. The Profile system accesses or writes
the property values to a backing store as needed. Like Membership and Roles, the Profile system is based on the
provider model, and the particular Profile provider
is responsible for serializing and deserializing the property values to some data store. The .NET Framework ships with
a SqlProfileProvider
class by default, which uses a SQL Server database table (aspnet_Profile
) as its backing store.
In this article we will examine the Profile system - how to define the user-specific properties and interact with them
programmatically from an ASP.NET page - as well as look at using the SqlProfileProvider
that ships with
.NET 2.0. In a future article we'll look at how to create and use a custom profile provider. Read on to learn more!
An Overview of the Profile System
The Membership system in ASP.NET was designed to create a standardized API for working with user accounts, a task faced by many web applications (refer back to Part 1 of this article series for a more in-depth look at Membership). While the Membership system encompasses core user-related properties - username, password, email address, and so on - oftentimes additional information needs to be captured for each user. Unfortunately, this additional information can differ wildly from application to application.
Rather than add additional user attributes to the Membership system, Microsoft instead created the Profile system to
handle additional user properties. The Profile system allows the additional, user-specific properties to be defined in
the Web.config
file and is responsible for persisting these values to some data store. How the user-specific
properties are serialized and deserialized to a backing store is the responsibility of the configured Profile provider
(see A Look at ASP.NET 2.0's Provider Model for more
information on the provider model and its uses). The default Profile provider - SqlProfileProvider
- serializes
the property values to the aspnet_Profile
table in a SQL Server database.
When working with the Profile system, keep in mind that it's sole purpose is to serve as a means for defining a set of user-specific properties and, through a particular provider, serialize those property values to some backing store.
Defining the User-Specific Properties
With the Profile system, the user-specific properties must be spelled out in the
Web.config
. For each property,
you can specify the name, data type, and how the data should be serialized. There are four serialization options:
ProviderSpecific
(the default) - the Profile provider is tasked with determining how to serialize the property valueString
- the property value is converted to a string representation when persisted by the Profile provider;Xml
- the property value is converted to an XML representation when persisted by the Profile providerBinary
- the property value is converted to a binary representation when persisted by the Profile provider
The user-specific properties defined in the Profile system can be simple scalar properties, scalar properties grouped into
complex types, or based on pre-existing complex types (such as a class). The names, types, and serialization instructions for
each Profile property is spelled out in the <profile>
element's <properties>
section.
For example, imagine that we were creating an online messageboard and wanted to allow each user to specify a homepage URL that
would appear alongside their posts. To capture this information, we could add the following profile property:
|
The <add>
element adds a Profile property named HomepageUrl
of type String and serialized
as a String. There are additional attributes that can be included in the <add>
element, such as
defaultValue
and readOnly
, among others. See the technical documentation on the
<add>
element for more information.
Scalar profile properties can be grouped using the <group>
element. For example, in addition to the
HomepageUrl
property we might also want to capture biographical information for our users, including their physical
location, birthdate, and programming language of choice. This information can appear as three scalar values, or can be grouped.
The following markup shows how to group these three properties into a group named Bio
.
|
In this grouping, the BirthDate
property (of type DateTime) was chosen to be serialized as XML. The Location
and ProgrammingLanguageOfChoice
properties don't have a serializeAs
attribute specified, meaning that
the Profile provider being used will ascertain how to serialize these properties. Note the type
for the
ProgrammingLanguageOfChoice
property. This is a type I created in the App_Code
folder. Specifically,
it's an enumeration that's defined with the following code:
|
Not only can Profile properties have enumerations as their type, but also can have custom classes as their type. In the
project that can be downloaded at the end of this article you'll find a very simple Address
class (also in
the App_Code
folder), that has properties representing an address:
|
With this class defined, I can add Profile properties of type Address
like so:
|
Both the BillingAddress
and ShippingAddress
properties are of type Address
.
BillingAddress
is serialized as XML, while ShippingAddress
is serialized using Binary
serialization. (In order to support binary serialization, the property's type must implement ISerializable
or have the Serializable
attribute applied.)
Working with the Profile Properties in Code
After defining your Profile properties, the ASP.NET engine automatically creates a
ProfileCommon
class that
has properties that map to the properties defined in Web.config
(which allows for IntelliSense and compile-time
type checking). This class is dynamically recreated whenever
the properties defined in Web.config
are modified and automatically works with
the currently logged on user's profile. (You can also turn on profile support for anonymous users, and later migrate their
profile data once they've logged on. But this topic is beyond the scope of this article, and will have to wait for a future
installment of this series!)
The custom ProfileCommon
class is accessible in the code portion of an ASP.NET page through the
HttpContext
object's Profile
property. For example, to read the currently logged on user's
HomepageUrl
property from an ASP.NET page, simply use Profile.HomepageUrl
. In fact, as soon as you
type in Profile
and hit period, IntelliSense brings up the various properties. Cool, eh? And the grouping functionality
works as you'd expect. Type in Profile
and hit period, and one of the properties is Bio
. Enter Bio
and hit period, and the three scalar properties appear in IntelliSense (BirthDate
, Location
, and
ProgrammingLanguageOfChoice
).

The download available at the end of this article has three Web pages worth examining. The first is /UsersOnly/Default.aspx
and shows the currently logged on user's Profile information. The second page, /UsersOnly/UpdateProfile.aspx
, allows the
currently logged on user to update his Profile data. The third page demonstrates the Profile
property's
GetProfile("username")
method, which can be used to get the
Profile data for a user other than the currently logged-on user. The page /UserList.aspx
lists all of the
users in the system, including their membership
information (their username, email, and so on) and HomepageUrl
Profile property value.
Specifying a Profile Provider
If you don't explicitly specify a Profile provider, the default provider -
SqlProfileProvider
- is used. This default
provider stores each user's property values in the aspnet_Profile
table. The aspnet_Profile
stores
each users profile settings in a single row in the table, using a rather inefficient means for storing the property names, data types,
and values. The table's schema follows:
aspnet_Profile | |
---|---|
UserId | uniqueidentifier ; PK; FK to aspnet_Users |
PropertyNames | ntext |
PropertyValuesString | ntext |
PropertyValuesBinary | image |
LastUpdatedDate | datetime |
For a given record, the PropertyNames
column contains each of the Profile property names along with how they are
serialized and where in the PropertyValuesString
or PropertyValuesBinary
columns their values
can be found. The values for each property are squished into the PropertyValuesString
or PropertyValuesBinary
columns, with String- and XML-serialized properties tucked in PropertyValuesString
and
Binary-serialized properties jammed in PropertyValuesBinary
.
With our Profile properties, the PropertyNames
column for a user will have a value like:
Bio.ProgrammingLanguageOfChoice:S:0:92:HomepageUrl:S:92:30:ShippingAddress:B:0:212:...
|
As you can see, each property name has the format PropertyName:B|S:StartIndex:Length, with
the B or S indicating whether the property's value can be found in PropertyValuesString
(S)
or PropertyValuesBinary
(B).
For example, the Bio.ProgrammingLanguageOfChoice
property value is stored in PropertyValuesString
(S)
starting at position 0 with a length of 92. The HomepageUrl
property is also serialized as a String and its value can be found at
index 92 with a length of 30.
Looking at the PropertyValuesString
column (where the String- and XML-serialized property values are stored),
we find:
<?xml version="1.0" encoding="utf-16"?><ProgrammingLanguages>CSharp</ProgrammingLanguages>http://www.datawebcontrols.com...
|
The value for the ProgrammingLanguageOfChoice
has been serialized as XML, starts at index 0, and is 92 characters long.
Likewise, the HomepageUrl
property starts at index 92 and has a length of 30 characters.
(The ShippingAddress
property uses Binary-serialization, and therefore is found in the PropertyValuesBinary
table. As you can see from inspecting the PropertyNames
column, the ShippingAddress
value
begins at index 0 and has a length of 212 bytes.)
All of the work of serializing Profile properties to the database is handled automatically by the SqlProfileProvider
.
The table and stored procedures used by the SqlProfileProvider
are automatically created when implementing the
SqlMembershipProvider
(see Part 1).
Performance and Querying Limitations of the SqlProfileProvider |
---|
Since the SqlProfileProvider squishes all of the Profile property values into one or two columns in a single row,
each time any Profile property is read or written to, all of the Profile values must be serialized or deserialized.
Additionally, the format of the data makes it next to impossible to query or report on the Profile information. Imagine that we
wanted to determine how many users in our system used C#. Since that information is jammed into the PropertyValuesString
column, determining this information would require expensive string processing or some routine that first "unpacks" the data into a normalized
structure.
An alternative to the built-in |
Conclusion
ASP.NET's Membership system makes creating and managing user accounts a piece of cake. Unfortunately, the Membership system only defined the most basic of user-specific properties - username, password, email address, and so on. If you need to capture additional information about your users, you must either use a custom solution (create your own data store, write code to read and write data to this store, and so on) or use the Profile system. The Profile system allows page developers to define a set of user-specific properties in
Web.config
. This information is automatically translated into a class (ProfileCommon
),
which is accessible through the HttpContext
class's Profile
property.
ASP.NET ships with a default Profile provider, SqlProfileProvider
, which stores the Profile property values
in the aspnet_Profile
table in a SQL Server database. This provide automatically handles all of the serialization and
deserialization work, you just need to programmatically read from and write to the user's Profile data through the
Profile
property.
Happy Programming!
Attachments
A Multipart Series on ASP.NET's Membership, Roles, and Profile |
---|
This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.
SqlMembershipProvider and the security Web controls.aspnet_regsql.exe ).SqlProfileProvider . |