10. The Profile API
Although your page
automatically gets the profile information for the current user, this
doesn't prevent you from retrieving and modifying the profiles of other
users. In fact, you have two tools to help you—the ProfileBase class and
the ProfileManager class.
The Profile object (provided by
the Page.Profile property) includes a useful GetProfile() method that
retrieves the profile information for a specific user by user name. Figure 4 shows an example with a Windows-authenticated user.
Here's the code that gets the profile:
Protected Sub cmdGet_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles cmdGet.Click
Dim currentProfile As ProfileCommon
currentProfile = Profile.GetProfile(txtUserName.Text)
lbl.Text = "This user lives in " & currentProfile.Address.Country
End Sub
GetProfile() returns a
ProfileCommon object. However, you won't find ProfileCommon in the .NET
class library. That's because ProfileCommon is a dynamically generated
class that ASP.NET creates to hold the profile information for your web
application. In this example, the profile defines a property named
Address, so that you can retrieve this information using the
ProfileCommon.Address property.
Notice that once you have a
ProfileCommon object, you can interact with it in the same way you
interact with the profile for the current user (after all, it's the same
object type). You can even make changes. The only difference is that
changes aren't saved automatically. If you want to save a change, you
need to call the Save() method of the ProfileCommon object.
ProfileCommon also adds the LastActivityDate and LastUpdatedDate
properties, which you can use to determine the last time a specific
profile was accessed and modified.
If you try to retrieve a
profile that doesn't exist, you won't get an error. Instead, you'll
simply end up with blank data (for example, empty strings). If you
change and save the profile, a new profile record will be created.
You can test for this
condition by examining the ProfileCommon.LastUpdatedDate property. If
the profile hasn't been created yet, this value will be a zero-date
value (in other words, day 1 on month 1 in year 0001). Here's the code
you'd use:
Protected Sub cmdGet_Click(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles cmdGet.Click
Dim currentProfile As ProfileCommon
currentProfile = Profile.GetProfile(txtUserName.Text)
If profile.LastUpdatedDate = DateTime.MinValue Then
lbl.Text = "No user match found."
Else
lbl.Text = "This user lives in " & currentProfile.Address.Country
End If
End Sub
If you need to perform
other tasks with profiles, you can use the ProfileManager class in the
System.Web.Profile namespace, which exposes the useful shared methods
described in Table 4.
Many of these methods work with a ProfileInfo class, which provides
information about a profile. The ProfileInfo includes the user name
(UserName), last update and last activity dates (LastUpdatedDate and
LastActivityDate), the size of the profile in bytes (Size), and whether
the profile is for an anonymous user (IsAnonymous). It doesn't provide
the actual profile values.
Table 4. ProfileManager Methods
Method | Description |
---|
DeleteProfile() | Deletes the profile for the user you specify. |
DeleteProfiles() | Deletes multiple profiles at once. You supply a collection of user names. |
DeleteInactiveProfiles() | Deletes
profiles that haven't been used since a time you specify. You also must
supply a value from the ProfileAuthenticationOption enumeration to
indicate what type of profiles you want to remove (All, Anonymous, or
Authenticated). |
GetNumberOfProfiles() | Returns
the number of profile records in the data source. You must supply a
value from the ProfileAuthenticationOption enumeration that indicates
whether you also want to see authenticated profiles (Authenticated),
anonymous profiles (Anonymous), or both (All). |
GetNumberOfInactive Profiles() | Returns
the number of profiles that haven't been used since the time you
specify. You must supply a value from the ProfileAuthenticationOption
enumeration. |
GetAllInactiveProfiles() | Retrieves
profile information for profiles that haven't been used since the time
you specify. You must supply a value from the
ProfileAuthenticationOption enumeration. The profiles are returned as
ProfileInfo objects. |
GetAllProfiles() | Retrieves
all the profile data from the data source as a collection of
ProfileInfo objects. You can choose what type of profiles you want to
retrieve (All, Anonymous, or Authenticated). You can also use an
overloaded version of this method that uses paging and retrieves only a
portion of the full set of records based on the starting index and page
size you request. |
FindProfilesByUser Name() | Retrieves
a collection of ProfileInfo objects matching a specific user name. The
SqlProfileProvider uses a LIKE clause when it attempts to match user
names, which means you can use wildcards such as the % symbol. For
example, if you search for the user name user%, you'll return values
such as user1, user2, user_guest, and so on. You can use an overloaded
version of this method that uses paging. |
FindInactiveProfilesByUserName() | Retrieves
profile information for profiles that haven't been used since the time
you specify. You can also filter out certain types of profiles (All,
Anonymous, or Authenticated) or look for a specific user name (with
wildcard matching). The return value is a collection of ProfileInfo
objects. |
For example, if you want to remove the profile for the current user, you need only a single line of code:
ProfileManager.DeleteProfile(User.Identity.Name)
And if you want to display
the full list of users in a web page (not including anonymous users),
just add a GridView with AutoGenerateColumns set to True and use this
code:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
gridProfiles.DataSource = ProfileManager.GetAllProfiles( _
ProfileAuthenticationOption.Authenticated)
gridProfiles.DataBind()
End Sub
Figure 5 shows the result.
11. Anonymous Profiles
So far, all the examples have
assumed that the user is authenticated before any profile information is
accessed or stored. Usually, this is the case. However, sometimes it's
useful to create a temporary profile for a new, unknown user. For
example, most e-commerce websites allow new users to begin adding items
to a shopping cart before registering. If you want to provide this type
of behavior and you choose to store shopping cart items in a profile,
you'll need some way to uniquely identify anonymous users.
NOTE
It's worth asking whether
it makes sense to store a shopping cart in a profile. It's a reasonable,
workable design, but many developers find it easier to explicitly
control how this type of information is stored in their database using
custom ADO.NET code instead of the profile feature.
ASP.NET provides an
anonymous identification feature that fills this gap. The basic idea is
that the anonymous identification feature automatically generates a
random identifier for any anonymous user. This random identifier stores
the profile information in the database, even though no user name is
available. The user name is tracked on the client side using a cookie
(or in the URL, if you've enabled cookieless mode). Once this cookie
disappears (for example, if the anonymous user closes and reopens the
browser), the anonymous session is lost and a new anonymous session is
created.
Anonymous identification has the
potential to leave a lot of abandoned profiles, which wastes space in
the database. For that reason, anonymous identification is disabled by
default. However, you can enable it using the
<anonymousIdentification> element in the web.config file, as shown
here:
<configuration>
...
<system.web>
<anonymousIdentification enabled="true" />
...
</system.web>
</configuration>
You also need to flag each
profile property that will be retained for anonymous users by adding the
allowAnonymous attribute and setting it to true. This allows you to
store just some basic information and restrict larger objects to
authenticated users.
<properties>
<add name="Address" type="Address" allowAnonymous="true" />
...
</properties>
If you're using a complex
type, the allowAnonymous attribute is an all-or-nothing setting. You
configure the entire object to support anonymous storage or not support
it.
The
<anonymousIdentification> element also supports numerous optional
attributes that let you set the cookie name and timeout, specify whether
the cookie will be issued only over an SSL connection, control whether
cookie protection (validation and encryption) is used to prevent
tampering and eavesdropping, and configure support for cookieless ID
tracking. Here's an example:
<anonymousIdentification enabled="true" cookieName=".ASPXANONYMOUS"
cookieTimeout="43200" cookiePath="/" cookieRequireSSL="false"
cookieSlidingExpiration="true" cookieProtection="All"
cookieless="UseCookies"/>
For more information, refer to the Visual Studio Help.
If you use anonymous
identification, it's a good idea to delete old anonymous sessions
regularly using the aspnet_Profile_DeleteInactiveProfiles stored
procedure, which you can run at scheduled intervals using the SQL Server
Agent. You can also delete old profiles using the ProfileManager class,
as described in the previous section.
|
|
11.1. Migrating Anonymous Profiles
One challenge that occurs with
anonymous profiles is what to do with the profile information when a
previously anonymous user logs in. For example, in an e-commerce website
a user might select several items and then register or log in to
complete the transaction. At this point, you need to make sure the
shopping cart information is copied from the anonymous user's profile to
the appropriate authenticated (user) profile.
Fortunately, ASP.NET
provides a solution through the ProfileModule.MigrateAnonymous event.
This event fires whenever an anonymous identifier is available (either
as a cookie or in the URL if you're using cookieless mode) and
the current user is authenticated. To handle the MigrateAnonymous
event, you need to add an event handler to the file that handles all
application events—the Global.asax file.
The basic technique when
handling the MigrateAnonymous event is to load the profile for the
anonymous user by calling Profile.GetProfile() and passing in the
anonymous ID, which is provided to your event handler through the
ProfileMigrateEventArgs.
Once you've loaded this
data, you can then transfer the settings to the new profile manually.
You can choose to transfer as few or as many settings as you want, and
you can perform any other processing that's required. Finally, your code
should remove the anonymous profile data from the database and clear
the anonymous identifier so the MigrateAnonymous event won't fire again.
For example:
Public Sub Profile_OnMigrateAnonymous(sender As Object, _
e As ProfileMigrateEventArgs)
' Get the anonymous profile.
Dim anonProfile As ProfileCommon = Profile.GetProfile(e.AnonymousID)
' Copy information to the authenticated profile
' (but only if there's information there).
If Not anonProfile.IsNullOfEmpty() Then
If anonProfile.Address.Name <> "" Then
Profile.Address = anonProfile.Address
End If
End If
' Delete the anonymous profile from the database.
' (You could decide to skip this step to increase performance
' if you have a dedicated job scheduled on the database server
' to remove old anonymous profiles.)
System.Web.Profile.ProfileManager.DeleteProfile(e.AnonymousID)
' Remove the anonymous identifier.
AnonymousIdentificationModule.ClearAnonymousIdentifier()
End Sub
You need to handle this task
with some caution. If you've enabled anonymous identification, the
MigrateAnonymous event fires every time a user logs in, even if the user
hasn't entered any information into the anonymous profile. That's a
problem—if you're not careful, you could easily overwrite the real
(saved) profile for the user with the blank anonymous profile. The
problem is further complicated by the fact that complex types (such as
the Address object) are created automatically by ASP.NET, so you can't
just check for a null reference to determine whether the user has
anonymous address information.
In the previous example, the
code tests for a missing Name property in the Address object. If this
information isn't part of the anonymous profile, no information is
migrated. A more sophisticated example might test for individual
properties separately or might migrate an anonymous profile only if the
information in the user profile is missing or outdated.