ASP.NET applications often have the need to store
user-specific information beyond the bare minimum username and password.
One way to solve this problem is to use the Session
collection. Session state has two
limitations: it isn't permanent (typically, a session times out after 20
minutes of inactivity), and it isn't strongly typed (in other words, you
need to know what's in the session collection and manually cast
references to the appropriate data types). ASP.NET 2.0 addresses these
limitations with a new framework for storing user-specific data called
profile settings.
Note
Need to store some custom user-specific information for long periods of time? Why not use the membership data provider to save and retrieve information without resorting to database code.
Profiles build on the same provider model that's used for membership and role management. Essentially, the profile provider takes care of storing all the user-specific information in some backend data store. Currently, ASP.NET includes a profile provider that's tailored for SQL Server.
Before you start using profiles, you should have a system in place for authenticating users. That's because personalized information needs to be linked to a specific user, so that you can retrieve it on subsequent visits. Typically, you'll use forms authentication, with the help of the ASP.NET membership services described in the lab "Easily Authenticate Users."
With profiles, you need to define the type of user-specific
information you want to store. In early builds, the WAT included a
tool for generating profile settings. However, this tool has
disappeared in later releases, and unless (or until) it returns, you
need to define your profile settings in the web.config file by hand. Here's an example
of a profile section that defines a single string named Fullname
:
Note
ASP. NET does include basic features that allow you to use personalization with anonymous users (see the "What about..." section of this lab for more information).
<?xml version="1.0"?> <configuration> <system.web> <profile> <properties> <add name="FullName" type="System.String" /> </properties> </profile> <!-- Other settings ommitted. --> </system.web> </configuration>
Initially, this doesn't seem any more useful than an application
setting. However, Visual Studio automatically generates a new class
based on your profile settings. You can access this class through the
Page.Profile
property. The other
benefit is the fact that ASP.NET stores this information in a backend
database, automatically retrieving it from the database at the
beginning of the request and writing it back at the end of the request
(if these operations are needed). In other words, profiles give you a
higher-level model for maintaining user-specific information that's
stored in a database.
In other words, assuming you've defined the FullName
property in the <profile>
section, you can set and
retrieve a user's name information using code like this:
Profile.FullName = "Joe Smythe" ... lblName.Text = "Hello " & Profile.FullName
Note that the Profile class is strongly typed. There's no need
to convert the reference, and Visual Studio's IntelliSense springs
into action when you type Profile
followed by the period.
Life gets even more interesting if you want to store a
full-fledged object. For example, imagine you create specialized
classes to track the products in a user's shopping basket. Example 4-6 shows a Basket
class that contains a collection of
BasketItem
objects, each
representing a separate product.
Example 4-6. Custom classes for a shopping cart
Imports System.Collections.Generic Public Class Basket Private _Items As New List(Of BasketItem) Public Property Items( ) As List(Of BasketItem) Get Return _Items End Get Set(ByVal value As List(Of BasketItem)) _Items = value End Set End Property End Class Public Class BasketItem Private _Name As String Public Property Name( ) As String Get Return _Name End Get Set(ByVal value As String) _Name = value End Set End Property Private _ID As String = Guid.NewGuid( ).ToString( ) Public Property ID( ) As String Get Return _ID End Get Set(ByVal value As String) _ID = value End Set End Property Public Sub New(ByVal name As String) _Name = name End Sub Public Sub New( ) ' Used for serialization. End Sub End Class
To use this class, you need to add it to the Code subdirectory so that it's compiled automatically. Then, to make it a part of the user profile, you need to define it in the web.config file, like this:
<profile> <properties> <add name="Basket" type="Basket" /> </properties> </profile>
With this information in place, it's easy to create a simple shopping cart test page. Figure 4-16 shows an example that lets you add and remove items. When the page is first loaded, it checks if there is a shopping basket for the current user, and if there isn't, it creates one. The user can then add items to the cart or remove existing items, using the Add and Remove buttons. Finally, the collection of shopping basket items is bound to a listbox every time the page is rendered, ensuring the page shows the current list of items in the basket. Example 4-7 shows the complete code.
Example 4-7. Testing a personalized shopping basket
<%@ Page language="VB" %> <script runat="server"> Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) If Profile.Basket Is Nothing Then Profile.Basket = New Basket( ) End Sub ' Put a new item in the basket. Sub cmdAdd_Click(ByVal sender As Object, ByVal e As System.EventArgs) Profile.Basket.Items.Add(New BasketItem(txtItemName.Text)) End Sub ' Remove the selected item. Sub cmdRemove_Click(ByVal sender As Object, ByVal e As System.EventArgs) For Each Item As BasketItem In Profile.Basket.Items If Item.ID = lstItems.SelectedItem.Value Then Profile.Basket.Items.Remove(Item) Return End If Next End Sub ' The page is being rendered. Create the list using data binding. Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) lstItems.DataSource = Profile.Basket.Items lstItems.DataTextField = "Name" lstItems.DataValueField = "ID" lstItems.DataBind( ) End Sub </script> <html> <head runat="server"> <title>Test Page</title> </head> <body> <form id="form1" runat="server"> <br /> <br /> <asp:ListBox ID="lstItems" Runat="server" Width="266px" Height="106px"></asp:ListBox><br /> <asp:TextBox ID="txtItemName" Runat="server" Width="266px"></asp:TextBox><br /> <asp:Button ID="cmdAdd" Runat="server" Width="106px" Text="Add New Item" OnClick="cmdAdd_Click" /> <asp:Button ID="cmdRemove" Runat="server" Width="157px" Text="Remove Selected Item" OnClick="cmdRemove_Click" /> </form> </body> </html>
Remember, profile information doesn't time out. That means that even if you rebuild and restart the web application, the shopping cart items will still be there, unless your code explicitly clears them. This makes profiles perfect for storing permanent user-specific information without worrying about the hassle of ADO.NET code.
...anonymous users? By default, you can only access profile information once a user has logged in. However, many web sites retain user-specific information even when users aren't logged in. For example, most online e-commerce shops let users start shopping immediately, and only force them to log in at checkout time. To implement this design (without resorting to session state), you need to use another new feature in ASP.NET—anonymous identification.
With anonymous identification, ASP.NET assigns a unique ID to every new user. This ID is stored in a persistent cookie, which means that even if a user waits several days before making a repeat visit, ASP.NET will still be able to identify the user and find the personalized information from the user's last visit. (The default expiration settings remove the cookie after about one week if the user hasn't returned.)
In order to use anonymous identification, you need to add the
<anonymousIdentification>
tag
to the web.config file, and you
need to explicitly indicate what profile information can be tracked
anonymously by flagging these properties with the allowAnonymous
attribute.
Here's an example with a revised web.config that stores shopping basket information for anonymous users:
<?xml version="1.0"?>
<configuration>
<system.web>
<anonymousIdentification enabled="true">
<profile>
<properties>
<add name="Basket" type="Basket" allowAnonymous="true"/>
</properties>
</profile>
<!-- Other settings ommitted. -->
</system.web>
</configuration>
Anonymous identification raises a few new considerations. The
most significant occurs in systems where an anonymous user needs to
log in at some point to complete an operation. In order to make sure
information isn't lost, you need to handle the PersonalizationModule.MigrateAnonymous
event
in the global.asax file.
You can then transfer information from the anonymous profile to the
new authenticated profile.
Get Visual Basic 2005: A Developer's Notebook now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.