Profile Providers

 

Microsoft ASP.NET 2.0 Providers: Introduction
Membership Providers
Role Providers
Site Map Providers
Session State Providers
Profile Providers
Web Event Providers
Web Parts Personalization Providers

Introduction

Profile providers provide the interface between Microsoft ASP.NET's profile service and profile data sources. ASP.NET 2.0 ships with one profile provider: SqlProfileProvider, which stores profile data in Microsoft SQL Server and Microsoft SQL Server Express databases.

The fundamental job of a profile provider is to write profile property values supplied by ASP.NET to a persistent profile data source, and to read the property values back from the data source when requested by ASP.NET. The Microsoft .NET Framework's System.Web.Profile namespace includes a class named ProfileProvider that defines the basic characteristics of a profile provider. ProfileProvider is prototyped as follows:

public abstract class ProfileProvider : SettingsProvider
{
    public abstract int DeleteProfiles
        (ProfileInfoCollection profiles);

    public abstract int DeleteProfiles (string[] usernames);

    public abstract int DeleteInactiveProfiles
        (ProfileAuthenticationOption authenticationOption,
        DateTime userInactiveSinceDate);

    public abstract int GetNumberOfInactiveProfiles
        (ProfileAuthenticationOption authenticationOption,
        DateTime userInactiveSinceDate);

    public abstract ProfileInfoCollection GetAllProfiles
        (ProfileAuthenticationOption authenticationOption,
        int pageIndex, int pageSize, out int totalRecords);

    public abstract ProfileInfoCollection GetAllInactiveProfiles
        (ProfileAuthenticationOption authenticationOption,
        DateTime userInactiveSinceDate, int pageIndex,
        int pageSize, out int totalRecords);

    public abstract ProfileInfoCollection FindProfilesByUserName
        (ProfileAuthenticationOption authenticationOption,
        string usernameToMatch, int pageIndex, int pageSize,
        out int totalRecords);

    public abstract ProfileInfoCollection
        FindInactiveProfilesByUserName (ProfileAuthenticationOption
        authenticationOption, string usernameToMatch,
        DateTime userInactiveSinceDate, int pageIndex,
        int pageSize, out int totalRecords);
}

A ProfileProvider-derived class must also implement the abstract methods and properties defined in System.Configuration.SettingsProvider, which is prototyped as follows:

public abstract class SettingsProvider : ProviderBase
{
    // Properties
    public abstract string ApplicationName { get; set; }

    // Methods
    public abstract SettingsPropertyValueCollection
        GetPropertyValues (SettingsContext context,
        SettingsPropertyCollection properties);

    public abstract void SetPropertyValues(SettingsContext context,
        SettingsPropertyValueCollection properties);
}

The two most important methods in a profile provider are the GetPropertyValues and SetPropertyValues methods inherited from SettingsProvider. These methods are called by ASP.NET to read property values from the data source, and write them back. Other profile provider methods play a lesser role, by performing administrative functions such as enumerating and deleting profiles.

When code executing within a request reads a profile property, ASP.NET calls the default profile provider's GetPropertyValues method. The context parameter passed to GetPropertyValues is a dictionary of key/value pairs containing information about the context in which GetPropertyValues was called. It contains the following keys:

  • UserName—User name or user ID of the profile to read.
  • IsAuthenticated—Indicates whether the requestor is authenticated.

The properties parameter contains a collection of SettingsProperty objects representing the property values ASP.NET is requesting. Each object in the collection represents one of the properties defined in the <profile> configuration section. GetPropertyValues's job is to return a SettingsPropertyValuesCollection supplying values for the properties in the SettingsPropertyCollection. If the property values have been persisted before, then GetPropertyValues can retrieve the values from the data source. Otherwise, it can return a SettingsPropertyValuesCollection that instructs ASP.NET to assign default values.

SetPropertyValues is the counterpart to GetPropertyValues. It's called by ASP.NET to persist property values in the profile data source. Like GetPropertyValues, it's passed a SettingsContext object containing a user name (or ID), and a Boolean indicating whether the user is authenticated. It's also passed a SettingsPropertyValueCollection containing the property values to be persisted. The format in which the data is persisted—and the physical storage medium that it's persisted in—is up to the provider. Obviously, the format in which SetPropertyValues persists profile data must be understood by the provider's GetProfileProperties method.

Profile property values are inherently scoped by user. For authenticated users, each set of persisted profile property values is accompanied by a user ID that uniquely identifies an authenticated user. For anonymous users, each set of persisted profile property values is accompanied by an anonymous user ID assigned by ASP.NET's anonymous identification service.

The following section documents the implementation of SqlProfileProvider, which derives from ProfileProvider.

SqlProfileProvider

SqlProfileProvider is the Microsoft profile provider for SQL Server databases. It stores profile data, using the schema documented in "Data Schema," and it uses the stored procedures documented in "Data Access." All knowledge of the database schema is hidden in the stored procedures, so that porting SqlProfileProvider to other database types requires little more than modifying the stored procedures. (Depending on the targeted database type, the ADO.NET code used to call the stored procedures might have to change, too. The Microsoft Oracle .NET provider, for example, uses a different syntax for named parameters.)

The ultimate reference for SqlProfileProvider is the SqlProfileProvider source code, which is found in SqlProfileProvider.cs. The sections that follow highlight key aspects of SqlProfileProvider's design and operation.

Provider Initialization

Initialization occurs in SqlProfileProvider.Initialize, which is called one time—when the provider is loaded—by ASP.NET. SqlProfileProvider.Initialize processes the applicationName, connectionStringName, and commandTimeout configuration attributes, and throws a ProviderException if unrecognized configuration attributes remain. It also reads the connection string identified by the connectionStringName attribute from the <connectionStrings> configuration section, and caches it in a private field, throwing a ProviderException if the attribute is empty or nonexistent, or if the attribute references a nonexistent connection string.

Data Schema

SqlProfileProvider stores profile data in the aspnet_Profile table of the provider database. Each record in aspnet_Profile corresponds to one user's persisted profile properties. Table 6-1 documents the aspnet_Profile table's schema.

Table 6-1. The aspnet_Profile table

Column Name Column Type Description
UserId uniqueidentifier ID of the user to which this profile data pertains
PropertyNames ntext Names of all property values stored in this profile
PropertyValuesString ntext Values of properties that could be persisted as text
PropertyValuesBinary image Values of properties that were configured to use binary serialization
LastUpdatedDate datetime Date and time this profile was last updated

The aspnet_Profile table has a foreign-key relationship with one other provider database table: aspnet_Users (see Table 1-3). The aspnet_Profile table's UserId column references the column of the same name in the aspnet_Users table, and it is used to scope profile data by user.

Additional Scoping of Profile Data

In addition to scoping profile data by user, SqlProfileProvider supports scoping by application name. Websites that register profile providers with identical applicationName attributes share profile data, whereas websites that register profile providers with unique applicationNames do not. Scoping by user name (or user ID) is facilitated by the user ID recorded with each set of persisted profile properties, whereas scoping by application name is facilitated by the application ID accompanying each user ID in the aspnet_Users table.

Data Access

SqlProfileProvider performs all database accesses through stored procedures. Table 6-2 lists the stored procedures that it uses.

Table 6-2. Stored procedures used by SqlProfileProvider

Stored Procedure Description
aspnet_Profile_DeleteInactiveProfiles Deletes profile data from the aspnet_Profile table for users whose last activity dates in the aspnet_Users table fall on or before the specified date.
aspnet_Profile_DeleteProfiles Deletes profile data from the aspnet_Profile table for the specified users.
aspnet_Profile_GetNumberOfInactiveProfiles Queries the aspnet_Profile table to get a count of profiles whose last activity dates (in the aspnet_Users table) fall on or before the specified date.
aspnet_Profile_GetProfiles Retrieves profile data from the aspnet_Profile table for users who match the criteria input to the stored procedure.
aspnet_Profile_GetProperties Retrieves profile data for the specified user.
aspnet_Profile_SetProperties Saves profile data for the specified user.

Stored procedure names are generally indicative of the SqlProfileProvider methods that call them. For example, ASP.NET calls the default profile provider's GetPropertyValues and SetPropertyValues methods to read and write profile data, and these methods in turn call the stored procedures named aspnet_Profile_GetProperties and aspnet_Profile_SetProperties, respectively.

Saving Profile Property Values

ASP.NET calls the default profile provider's SetPropertyValues method to persist profile properties for a given user. SqlProfileProvider.SetPropertyValues performs the following actions:

  1. Extracts the user name and a value indicating whether the user is authenticated, from the SettingsContext parameter passed to it.
  2. Formats the property values for saving, by iterating through the items in the SettingsPropertyValuesCollection passed to it, and initializing three variables—names, values, and buf—with the values to be written to the database. (For more information about the format of these variables, see "Persistence Format.")
  3. Calls the stored procedure aspnet_Profile_SetProperties to write names, values, and buf to the PropertyNames, PropertyValuesString, and PropertyValuesBinary fields of the provider database, respectively.

SqlProfileProvider.SetPropertyValues delegates the task of initializing names, values, and buf from the SettingsPropertyValuesCollection input to it, to a helper method named PrepareDataForSaving. That method employs the following logic:

  1. Checks the property values in the collection, and returns without doing anything if none of the property values are dirty, or if the collection contains dirty property values, but the user is not authenticated and none of the dirty property values have allowAnonymous attributes equal to true. This optimization prevents round-tripping to the database in cases where no data has changed.
  2. Iterates through the collection, skipping property values lacking allowAnonymous attributes equal to true if the user is not authenticated, as well as property values that are not dirty and whose UsingDefaultValue property is true. In the first case, it is the responsibility of the provider to enforce the distinction between anonymous and authenticated use of profile properties. SqlProfileProvider does so by simply ignoring any profile properties that have not been marked with the allowAnonymous attribute when the current user is anonymous. The second case is a bit more subtle. A profile property can be assigned a default value in the profile definition. Alternatively, a profile property value may have been fetched from the database but never modified in code. If the profile property is using the default value from the profile definition, then the provider avoids the overhead of serializing it, as well as the storage overhead of storing the value in the database. After all, the value can always be reconstituted from the defaultValue attribute in the profile definition.
  3. Processes the remaining property values in the SettingsPropertyValueCollection, by appending each property's SerializedValue property to any data already in values or buf. String SerializedValues are appended to values, whereas non-string SerializedValues are appended to buf. In either case, information denoting where SerializedValue was stored is written to names. As an optimization, if it finds that a property value's Deserialized property is true and it's PropertyValue property is null, the helper method doesn't record SerializedValue at all; instead, it simply records a length of -1 for that property value in names.

When called by SqlProfileProvider.SetPropertyValues, aspnet_Profile_SetProperties performs the following actions:

  1. Calls the stored procedure aspnet_Applications_CreateApplication to convert the application name input to it into an application ID.
  2. Queries the aspnet_Users table to convert the user name input to it into a user ID. If the query returns no records, aspnet_Profile_SetProperties calls aspnet_Users_CreateUser to add the user to the aspnet_Users table and return a user ID.
  3. Updates the user's last activity date in the aspnet_Users table with the current date and time.
  4. Either updates an existing record in the aspnet_Profile table if an entry for the specified user already exists, or inserts a new one.

aspnet_Profile_SetProperties performs all these steps within a transaction, to ensure that changes are committed as a group or not at all.

Loading Profile Property Values

ASP.NET calls the default profile provider's GetPropertyValues method to retrieve profile properties for a given user. SqlProfileProvider.GetPropertyValues performs the following actions:

  1. Creates an empty SettingsPropertyValueCollection to hold the SettingsPropertyValues that will ultimately be returned to ASP.NET.
  2. Extracts the user name from the SettingsContext parameter passed to it.
  3. Iterates through the SettingsPropertyCollection passed to it and, for each SettingsProperty it finds, adds a corresponding SettingsPropertyValue to the SettingsPropertyValueCollection created in the first step. If a SettingsProperty lacks a serializeAs attribute, SqlProfileProvider.GetPropertyValues sets the SerializeAs property of the corresponding SettingsPropertyValue to SettingsSerializeAs.String if the property value is a string or primitive, or to SettingsSerializeAs.Xml if the property value is of any other type.
  4. Calls the stored procedure aspnet_Profile_GetProperties to retrieve the user's profile data from the provider database. SqlProfileProvider.GetPropertyValues copies the data returned by the stored procedure into variables named names, values, and buf. (For more information about the format of these variables, see "Persistence Format.")
  5. Parses the data stored in names, values, and buf, and uses it to initialize the SettingsPropertyValues.
  6. Returns the SettingsPropertyValuesCollection containing the initialized SettingsPropertyValues to ASP.NET.

SqlProfileProvider.GetPropertyValues delegates the task of parsing the data retrieved from the database and using it to initialize the SettingsPropertyValues, to a helper method named ParseDataFromDB. That method decomposes the names array into items denoting the names and locations of profile properties. For each item in names, ParseDataFromDB retrieves the corresponding property value from values or buf, and writes it to the SerializedValue property of the corresponding SettingsPropertyValue. As an optimization, if the length recorded for a profile property in names is –1, and the property represents a reference type (as opposed to a value type), the helper method sets the corresponding SettingsPropertyValue's PropertyValue property to null and the Deserialized property to true, effectively returning a null property value to ASP.NET.

When called by SqlProfileProvider.GetPropertyValues, aspnet_Profile_GetProperties performs the following actions:

  1. Queries the aspnet_Applications table to convert the application name input to it into an application ID.
  2. Queries the aspnet_Users table to convert the user name input to it into a user ID.
  3. Queries the aspnet_Profile table for the PropertyNames, PropertyValuesString, and PropertyValuesBinary fields for the specified user.
  4. Updates the user's last activity date in the aspnet_Users table with the current date and time.

If anything goes wrong along the way—if, for example, the application ID input to the stored procedure is invalid—aspnet_Profile_GetProperties return no records, indicating that no profile data exists for the specified user.

Persistence Format

SqlProfileProvider persists profile properties in three fields of the aspnet_Profile table: PropertyNames, PropertyValuesString, and PropertyValuesBinary. The following is a synopsis of what's stored in each field:

  • PropertyNames holds a string value containing information about the profile property values present in the PropertyValuesString and PropertyValuesBinary fields. The string holds a colon-delimited list of items; each item denotes one property value, and it is encoded in the following format:

    Name:B|S:StartPos:Length
    

    Name is the property value's name. The second parameter, which is either B (for "binary") or S (for "string"), indicates whether the corresponding property value is stored in the PropertyValuesString field (S) or the PropertyValuesBinary field (B). StartPos and Length indicate the starting position (0-based) of the corresponding property value within these fields, and the length of the data, respectively. A length of -1 indicates that the property is a reference type, and that its value is null.

  • PropertyValuesString stores profile property values persisted as strings. This includes property values serialized by the .NET Framework's XML serializer, and property values serialized using string type converters. The "S" values in the PropertyNames field contain the offsets and lengths needed to decompose PropertyValuesString into individual property values.

  • PropertyValuesBinary stores profile property values persisted in binary format—that is, profile properties that were serialized using the .NET Framework's binary serializer. The "B" values in the PropertyNames field contain the offsets and lengths needed to decompose PropertyValuesBinary into individual property values.

Note that profile providers are not required to persist data in this format or any other format. The format in which profile data is stored is left to the discretion of the person or persons writing the provider.

Differences Between the Published Source Code and the .NET Framework's SqlProfileProvider

The published source code for SqlProfileProvider differs from the .NET Framework version in the following respects:

  • Declarative and imperative CAS checks were commented out. Because the source code can be compiled standalone, and thus will run as user code rather than trusted code in the global assembly cache, the CAS checks are not necessary.
  • Calls to internal helper methods that integrate with ETW tracing have been commented out in the published version.
  • The internal helper methods for packaging data and unpacking data have been cloned into the published provider, so that you can see the logic that is used in the ParseDataFromDB and PrepareDataForSaving methods.
  • Because the standalone provider compiles into a regular assembly, in partial trust applications you will be able to use only string serialization or XML serialization. Binary serialization requires a specific permission assertion (SecurityPermission.SerializationFormatter) that has been commented out in the code for the cloned versions of ParseDataFromDB and PrepareDataForSaving.

Return to part 5, Session State Providers.

Go on to part 7, Web Event Providers.

© Microsoft Corporation. All rights reserved.