Using Profiles


The ASP.NET 2.0 Framework provides you with an alternative to using cookies or Session state to store user information: the Profile object. The Profile object provides you with a strongly typed, persistent form of session state.

You create a Profile by defining a list of Profile properties in your application root web configuration file. The ASP.NET Framework dynamically compiles a class that contains these properties in the background.

For example, the web configuration file in Listing 22.19 defines a Profile that contains three properties: firstName, lastName, and numberOfVisits.

Listing 22.19. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <profile>     <properties>       <add name="firstName" />       <add name="lastName" />       <add name="numberOfVisits" type="Int32" defaultValue="0" />     </properties>   </profile> </system.web> </configuration> 

When you define a Profile property, you can use any of the following attributes:

  • name Enables you to specify the name of the property.

  • type Enables you to specify the type of the property. The type can be any custom type, including a custom component that you define in the App_Code folder. (The default type is string.)

  • defaultValue Enables you to specify a default value for the property.

  • readOnly Enables you to create a read-only property. (The default value is false.)

  • serializeAs Enables you to specify how a property is persisted into a static representation. Possible values are Binary, ProviderSpecific, String, and Xml. (The default value is ProviderSpecific.)

  • allowAnonymous Enables you to allow anonymous users to read and set the property. (The default value is false.)

  • provider Enables you to associate the property with a particular Profile provider.

  • customProviderData Enables you to pass custom data to a Profile provider.

After you define a Profile in the web configuration file, you can use the Profile object to modify the Profile properties. For example, the page in Listing 22.20 enables you to modify the firstName and lastName properties with a form. Furthermore, the page automatically updates the numberOfVisits property each time the page is requested (see Figure 22.7).

Figure 22.7. Displaying Profile information.


Listing 22.20. ShowProfile.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Private Sub Page_PreRender()         lblFirstname.Text = Profile.firstName         lblLastName.Text = Profile.lastName         Profile.numberOfVisits = Profile.numberOfVisits + 1         lblNumberOfVisits.Text = Profile.numberOfVisits.ToString()     End Sub     Protected Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs)         Profile.firstName = txtNewFirstName.Text         Profile.lastName = txtNewLastName.Text     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Profile</title> </head> <body>     <form  runat="server">     <div>     First Name:     <asp:Label                  Runat="server" />     <br /><br />     Last Name:     <asp:Label                  Runat="server" />     <br /><br />     Number of Visits:     <asp:Label                  Runat="server" />     <hr />     <asp:Label                  Text="New First Name:"         AssociatedControl         Runat="server" />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Label                  Text="New Last Name:"         AssociatedControl         Runat="server" />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Button                  Text="Update Profile"         OnClick="btnUpdate_Click"         Runat="server" />     </div>     </form> </body> </html> 

Notice that Profile properties are exposed as strongly typed properties. The numberOfVisits property, for example, is exposed as an integer property because you defined it as an integer property.

It is important to understand that Profile properties are persistent. If you set a Profile property for a user, and that user does not return to your web site for 500 years, the property retains its value. Unlike Session state, when you assign a value to a Profile property, the value does not evaporate after a user leaves your website.

The Profile object uses the Provider model. The default Profile provider is the SqlProfileProvider. By default, this provider stores the Profile data in a Microsoft SQL Server 2005 Express database named ASPNETDB.mdf, located in your application's App_Code folder. If the database does not exist, it is created automatically the first time that you use the Profile object.

By default, you cannot store Profile information for an anonymous user. The ASP.NET Framework uses your authenticated identity to associate Profile information with you. You can use the Profile object with any of the standard types of authentication supported by the ASP.NET Framework, including both Forms and Windows authentication. (Windows authentication is enabled by default.)

Note

Later in this section, you learn how to store Profile information for anonymous users.


Creating Profile Groups

If you need to define a lot of Profile properties, then you can make the properties more manageable by organizing the properties into groups. For example, the web configuration file in Listing 22.21 defines two groups named Preferences and ContactInfo.

Listing 22.21. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <profile>     <properties>       <group name="Preferences">         <add name="BackColor" defaultValue="lightblue"/>         <add name="Font" defaultValue="Arial"/>       </group>       <group name="ContactInfo">         <add name="Email" defaultValue="Your Email"/>         <add name="Phone" defaultValue="Your Phone"/>       </group>     </properties>   </profile> </system.web> </configuration> 

The page in Listing 22.22 illustrates how you can set and read properties in different groups.

Listing 22.22. ShowProfileGroups.aspx

<%@ Page Language="VB" %> <%@ Import Namespace="System.Drawing" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Private  Sub Page_Load()         ' Display Contact Info         lblEmail.Text = Profile.ContactInfo.Email         lblPhone.Text = Profile.ContactInfo.Phone         ' Apply Preferences         Dim pageStyle As New Style()         pageStyle.BackColor = ColorTranslator.FromHtml(Profile.Preferences.BackColor)         pageStyle.Font.Name = Profile.Preferences.Font         Header.StyleSheet.CreateStyleRule(pageStyle, Nothing, "html")     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Untitled Page</title> </head> <body>     <form  runat="server">     <div>     Email:     <asp:Label                  Runat="server" />     <br /><br />     Phone:     <asp:Label                  Runat="server" />     </div>     </form> </body> </html> 

Supporting Anonymous Users

By default, anonymous users cannot modify Profile properties. The problem is that the ASP.NET Framework has no method of associating Profile data with a particular user unless the user is authenticated.

If you want to enable anonymous users to modify Profile properties, you must enable a feature of the ASP.NET Framework called Anonymous Identification. When Anonymous Identification is enabled, a unique identifier (a GUID) is assigned to anonymous users and stored in a persistent browser cookie.

Note

You can enable cookieless anonymous identifiers. Cookieless anonymous identifiers work just like cookieless sessions: The anonymous identifier is added to the page URL instead of a cookie. You enable cookieless anonymous identifiers by setting the cookieless attribute of the anonymousIdentification element in the web configuration file to the value UseURI or AutoDetect.


Furthermore, you must mark all Profile properties that you want anonymous users to be able to modify with the allowAnonymous attribute. For example, the web configuration file in Listing 22.23 enables Anonymous Identification and defines a Profile property that can be modified by anonymous users.

Listing 22.23. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <authentication mode="Forms" />   <anonymousIdentification enabled="true" />   <profile>     <properties>       <add         name="numberOfVisits"         type="Int32"         defaultValue="0"         allowAnonymous="true" />     </properties>   </profile> </system.web> </configuration> 

The numberOfVisits property defined in Listing 22.23 includes the allowAnonymous attribute. Notice that the web configuration file also enables Forms authentication. When Forms authentication is enabled, and you don't log in, then you are an anonymous user.

The page in Listing 22.24 illustrates how you modify a Profile property when Anonymous Identification is enabled.

Listing 22.24. ShowAnonymousIdentification.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Private Sub Page_PreRender()         lblUserName.Text = Profile.UserName         lblIsAnonymous.Text = Profile.IsAnonymous.ToString()         Profile.numberOfVisits = Profile.numberOfVisits + 1         lblNumberOfVisits.Text = Profile.numberOfVisits.ToString()     End Sub     Protected Sub btnLogin_Click(ByVal sender As Object, ByVal e As EventArgs)         FormsAuthentication.SetAuthCookie("Bob", False)         Response.Redirect(Request.Path)     End Sub     Protected Sub btnLogout_Click(ByVal sender As Object, ByVal e As EventArgs)         FormsAuthentication.SignOut()         Response.Redirect(Request.Path)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Anonymous Identification</title> </head> <body>     <form  runat="server">     <div>     User Name:     <asp:Label                  Runat="server" />     <br />     Is Anonymous:     <asp:Label                  Runat="server" />     <br />     Number Of Visits:     <asp:Label                  Runat="server" />     <hr />     <asp:Button                  Text="Reload"         Runat="server" />     <asp:Button                  Text="Login"         OnClick="btnLogin_Click"         Runat="server" />     <asp:Button                  Text="Logout"         OnClick="btnLogout_Click"         Runat="server" />     </div>     </form> </body> </html> 

Each time that you request the page in Listing 22.24, the numberOfVisits Profile property is incremented and displayed. The page includes three buttons: Reload, Login, and Logout (see Figure 22.8).

Figure 22.8. Creating an anonymous profile.


The page also displays the value of the Profile.UserName property. This property represents either the current username or the anonymous identifier. The value of the numberOfVisits Profile property is tied to the value of the Profile.UserName property.

You can click the Reload button to quickly reload the page and increment the value of the numberOfVisits property.

If you click the Login button, then the Profile.UserName property changes to the value Bob. The numberOfVisits property is reset.

If you click the Logout button, then the Profile.UserName property switches back to your anonymous identifier. The numberOfVisits property reverts to its previous value.

Migrating Anonymous Profiles

In the previous section, you saw that all profile information is lost when a user transitions from anonymous to authenticated. For example, if you store a shopping cart in the Profile object and a user logs in, then all the shopping cart items are lost.

You can preserve the value of Profile properties when a user transitions from anonymous to authenticated by handling the MigrateAnonymous event in the Global.asax file. This event is raised when an anonymous user that has a profile logs in.

For example, the MigrateAnonymous event handler in Listing 22.25 automatically copies the values of all anonymous Profile properties to the user's current authenticated profile.

Listing 22.25. Global.asax

[View full width]

<%@ Application Language="VB" %> <script runat="server">     Public Sub Profile_OnMigrateAnonymous(ByVal sender As Object, ByVal args As  ProfileMigrateEventArgs)         ' Get anonymous profile         Dim anonProfile As ProfileCommon = Profile.GetProfile(args.AnonymousID)         ' Copy anonymous properties to authenticated         For Each prop As SettingsProperty In ProfileBase.Properties             Profile(prop.Name) = anonProfile(prop.Name)         Next         ' Kill the anonymous profile         ProfileManager.DeleteProfile(args.AnonymousID)         AnonymousIdentificationModule.ClearAnonymousIdentifier()     End Sub </script> 

The anonymous Profile associated with the user is retrieved when the user's anonymous identifier is passed to the Profile.GetProfile() method. Next, each Profile property is copied from the anonymous Profile to the current Profile. Finally, the anonymous Profile is deleted and the anonymous identifier is destroyed. (If you don't destroy the anonymous identifier, then the MigrateAnonymous event continues to be raised with each page request after the user authenticates.)

Inheriting a Profile from a Custom Class

Instead of defining a list of Profile properties in the web configuration file, you can define Profile properties in a separate class. For example, the class in Listing 22.26 contains two properties named FirstName and LastName.

Listing 22.26. App_Code\SiteProfile.vb

Imports SystemSiteProfile.vb Imports System.Web.Profile Public Class SiteProfile     Inherits ProfileBase     Private _firstName As String = "Your First Name"     Private _lastName As String = "Your Last Name"     <SettingsAllowAnonymous(True)> _     Public Property FirstName() As String         Get             Return _firstName         End Get         Set(ByVal Value As String)             _firstName = value         End Set     End Property     <SettingsAllowAnonymous(True)> _     Public Property LastName() As String         Get             Return _lastName         End Get         Set(ByVal Value As String)             _lastName = value         End Set     End Property End Class 

Notice that the class in Listing 22.26 inherits from the BaseProfile class.

After you declare a class, you can use it to define a profile by inheriting the Profile object from the class in the web configuration file. The web configuration file in Listing 22.27 uses the inherits attribute to inherit the Profile from the SiteProfile class.

Listing 22.27. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <anonymousIdentification enabled="true" />   <profile inherits="SiteProfile" /> </system.web> </configuration> 

After you inherit a Profile in the web configuration file, you can use the Profile in the normal way. You can set or read any of the properties that you defined in the SiteProfile class by accessing the properties through the Profile object.

Note

The CD that accompanies this book includes a page named ShowSiteProfile.aspx, which displays the Profile properties defined in Listing 22.27.


Note

If you inherit Profile properties from a class and define Profile properties in the web configuration file, then the two sets of Profile properties are merged.


When you define Profile properties in a class, you can decorate the properties with the following attributes:

  • SettingsAllowAnonymous Enables you to allow anonymous users to read and set the property.

  • ProfileProvider Enables you to associate the property with a particular Profile provider.

  • CustomProviderData Enables you to pass custom data to a Profile provider.

For example, both properties declared in the SiteProfile class in Listing 22.28 include the SettingsAllowAnonymous attribute, which allows anonymous users to read and modify the properties.

Creating Complex Profile Properties

To this point, we've used the Profile properties to represent simple types such as strings and integers. You can use Profile properties to represent more complex types such as a custom ShoppingCart class.

For example, the class in Listing 22.28 represents a simple shopping cart.

Listing 22.28. App_Code\ShoppingCart.vb

[View full width]

Imports System Imports System.Collections.Generic Imports System.Web.Profile Namespace AspNetUnleashed     Public Class ShoppingCart         Private _items As New List(Of CartItem)()         Public ReadOnly Property Items() As List(Of CartItem)             Get                 Return _items             End Get         End Property     End Class     Public Class CartItem         Private _name As String         Private _price As Decimal         Private _description As String         Public Property Name() As String             Get                 Return _name             End Get             Set(ByVal Value As String)                 _name = value             End Set         End Property         Public Property Price() As Decimal             Get                 Return _price             End Get             Set(ByVal Value As Decimal)                 _price = value             End Set         End Property         Public Property Description() As String             Get                 Return _description             End Get             Set(ByVal Value As String)                 _description = value             End Set         End Property         Public Sub New()         End Sub         Public Sub New(ByVal name As String, ByVal price As Decimal, ByVal description As  String)             _name = name             _price = price             _description = description         End Sub     End Class End Namespace 

The file in Listing 22.28 actually contains two classes: the ShoppingCart class and the CartItem class. The ShoppingCart class exposes a collection of CartItem objects.

The web configuration file in Listing 22.29 defines a Profile property named ShoppingCart that represents the ShoppingCart class. The type attribute is set to the fully qualified name of the ShoppingCart class.

Listing 22.29. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <profile>     <properties>       <add name="ShoppingCart" type="AspNetUnleashed.ShoppingCart" />     </properties>   </profile> </system.web> </configuration> 

Finally, the page in Listing 22.30 uses the Profile.ShoppingCart property. The contents of the ShoppingCart are bound and displayed in a GridView control. The page also contains a form that enables you to add new items to the ShoppingCart (see Figure 22.9).

Figure 22.9. Storing a shopping cart in a profile.


Listing 22.30. ShowShoppingCart.aspx

[View full width]

<%@ Page Language="VB" %> <%@ Import Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Private Sub Page_PreRender()         grdShoppingCart.DataSource = Profile.ShoppingCart.Items         grdShoppingCart.DataBind()     End Sub     Protected Sub btnAdd_Click(ByVal sender As Object, ByVal e As EventArgs)         Dim NewItem As New CartItem(txtName.Text, Decimal.Parse(txtPrice.Text),  txtDescription.Text)         Profile.ShoppingCart.Items.Add(NewItem)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show ShoppingCart</title> </head> <body>     <form  runat="server">     <div>     <asp:GridView                  EmptyDataText="There are no items in your shopping cart"         Runat="server" />     <br />     <fieldset>     <legend>Add Product</legend>     <asp:Label                  Text="Name:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Label                  Text="Price:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Label                  Text="Description:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Button                  Text="Add To Cart"         Runat="server" OnClick="btnAdd_Click" />     </fieldset>     </div>     </form> </body> </html> 

If you want to take control over how complex properties are stored, you can modify the value of the serializeAs attribute associated with a Profile property. The serializeAs attribute accepts the following four values:

  • Binary

  • ProviderSpecific

  • String

  • Xml

The default value, when using the SqlProfileProvider, is ProviderSpecific. In other words, the SqlProfileProvider decides on the best method for storing properties. In general, simple types are serialized as strings and complex types are serialized with the XML Serializer.

One disadvantage of the XML Serializer is that it produces a more bloated representation of a property than the Binary Serializer. For example, the results of serializing the ShoppingCart class with the XML Serializer are contained in Listing 22.31:

Listing 22.31. Serialized Shopping Cart

<?xml version="1.0" encoding="utf-16"?> <ShoppingCart xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance  xmlns:xsd="http://www.w3.org/2001/XMLSchema">   <Items>     <CartItem>       <Name>First Product</Name>       <Price>2.99</Price>       <Description>The First Product</Description>     </CartItem>     <CartItem>       <Name>Second Product</Name>       <Price>2.99</Price>       <Description>The Second Product</Description>     </CartItem>   </Items> </ShoppingCart> 

If you want to serialize a Profile property with the Binary Serializer (and save some database space) then you need to do two things. First, you need to indicate in the web configuration file that the Profile property should be serialized with the Binary Serializer. Furthermore, you need to mark the class that the Profile property represents as serializable.

The modified ShoppingClass (named BinaryShoppingCart) in Listing 22.32 includes a Serializable attribute. Notice that both the BinaryShoppingCart and BinaryCartItem classes are decorated with the Serializable attribute.

Listing 22.32. App_Code\BinaryShoppingCart.vb

[View full width]

Imports System Imports System.Collections.Generic Imports System.Web.Profile Namespace AspNetUnleashed     <Serializable()> _     Public Class BinaryShoppingCart         Private _items As New List(Of BinaryCartItem)()         Public ReadOnly Property Items() As List(Of BinaryCartItem)             Get                 Return _items             End Get         End Property     End Class     <Serializable()> _     Public Class BinaryCartItem         Private _name As String         Private _price As Decimal         Private _description As String         Public Property Name() As String             Get                 Return _name             End Get             Set(ByVal Value As String)                 _name = Value             End Set         End Property         Public Property Price() As Decimal             Get                 Return _price             End Get             Set(ByVal Value As Decimal)                 _price = Value             End Set         End Property         Public Property Description() As String             Get                 Return _description             End Get             Set(ByVal Value As String)                 _description = Value             End Set         End Property         Public Sub New()         End Sub         Public Sub New(ByVal name As String, ByVal price As Decimal, ByVal description As  String)             _name = name             _price = price             _description = description         End Sub     End Class End Namespace 

The Profile in the web configuration file in Listing 22.33 includes a property that represents the BinaryShoppingCart class. Notice that the property includes a serializeAs attribute that has the value Binary. If you don't include this attribute, the BinaryShoppingCart will be serialized as XML.

Listing 22.33. Web.Config

<?xml version="1.0"?> <configuration> <system.web>   <profile>     <properties>       <add         name="ShoppingCart"         type="AspNetUnleashed.BinaryShoppingCart"         serializeAs="Binary" />     </properties>   </profile> </system.web> </configuration> 

Note

The CD that accompanies this book includes a page named ShowBinaryShoppingCart.aspx that displays the BinaryShoppingCart.


Saving Profiles Automatically

A profile is loaded from its profile provider the first time that a property from the profile is accessed. For example, if you use a Profile property in a Page_Load() handler, then the profile is loaded during the Page Load event. If you use a Profile property in a Page_PreRender() handler, then the Profile is loaded during the page PreRender event.

If a Profile property is modified, then the Profile is saved automatically at the end of page execution. The ASP.NET Framework can detect automatically when certain types of properties are changed but not others. In general, the ASP.NET Framework can detect changes made to simple types but not to complex types.

For example, if you access a property that exposes a simple type such as a string, integer, or Datetime, then the ASP.NET Framework can detect when the property has been changed. In that case, the framework sets the Profile.IsDirty property to the value true. At the end of page execution, if a profile is marked as dirty, then the profile is saved automatically.

The ASP.NET Framework cannot detect when a Profile property that represents a complex type has been modified. For example, if your profile includes a property that represents a custom ShoppingCart class, then the ASP.NET Framework has no way of determining when the contents of the ShoppingCart class have been changed.

The ASP.NET Framework errs on the side of caution. If you access a complex Profile property at alleven if you simply read the propertythe ASP.NET Framework sets the Profile.IsDirty property to the value true. In other words, if you read a complex property, the profile is always saved at the end of page execution.

Because storing a profile at the end of each page execution can be an expensive operation, the ASP.NET Framework provides you with two methods of controlling when a profile is saved.

First, you can take the responsibility of determining when a profile is saved. The web configuration file in Listing 22.34 disables the automatic saving of profiles by setting the autoSaveEnabled property to the value false.

Listing 22.34. Web.Config

<?xml version="1.0"?> <configuration>   <system.web>   <profile automaticSaveEnabled="false">     <properties>       <add name="ShoppingCart" type="AspNetUnleashed.ShoppingCart"/>     </properties>   </profile> </system.web> </configuration> 

After you disable the automatic saving of profiles, you must explicitly call the Profile.Save() method to save a profile after you modify it. For example, the btnAdd_Click() method in Listing 22.35 explicitly calls the Profile.Save() method when a new item has been added to the shopping cart.

Listing 22.35. ShowExplicitSave.aspx

[View full width]

<%@ Page Language="VB" %> <%@ Import Namespace="AspNetUnleashed" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Sub Page_PreRender()         grdShoppingCart.DataSource = Profile.ShoppingCart.Items         grdShoppingCart.DataBind()     End Sub     Sub btnAdd_Click(sender As object, e As EventArgs)         Dim newItem as new CartItem(txtName.Text, decimal.Parse(txtPrice.Text) ,txtDescription.Text)         Profile.ShoppingCart.Items.Add(newItem)         ' Explicitly Save Shopping Cart         Profile.Save()     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Explicit Save</title> </head> <body>     <form  runat="server">     <div>     <asp:GridView                  EmptyDataText="There are no items in your shopping cart"         Runat="server" />     <br />     <fieldset>     <legend>Add Product</legend>     <asp:Label                  Text="Name:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Label                  Text="Price:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Label                  Text="Description:"         AssociatedControl         Runat="server" />     <br />     <asp:TextBox                  Runat="server" />     <br /><br />     <asp:Button                  Text="Add To Cart"         OnClick="btnAdd_Click"         Runat="server" />     </fieldset>     </div>     </form> </body> </html> 

As an alternative to disabling the automatic saving of profiles, you can write custom logic to control when a profile is saved by handling the ProfileAutoSaving event in the Global.asax file. For example, the Global.asax file in Listing 22.36 saves a profile only when the Profile.ShoppingCart.HasChanged property has been assigned the value true.

Listing 22.36. Global.asax

[View full width]

<%@ Application Language="VB" %> <script runat="server">     Public Sub Profile_ProfileAutoSaving(ByVal s As Object, ByVal e As  ProfileAutoSaveEventArgs)         If Profile.ShoppingCart.HasChanged Then             e.ContinueWithProfileAutoSave = True         Else             e.ContinueWithProfileAutoSave = False         End If     End Sub </script> 

Note

The CD that accompanies this book includes the shopping cart class and ASP.NET page that accompany the Global.asax file in Listing 22.37. The class is named ShoppingCartHasChanged.vb and the page is named ShowShoppingCartHasChanged.aspx. You'll need to modify the web configuration file so that the profile inherits from the ShoppingCartHasChanged class.


Accessing Profiles from Components

You can access the Profile object from within a component by referring to the HttpContext.Profile property. However, you must cast the value of this property to an instance of the ProfileCommon object before you access its properties.

For example, the web configuration file in Listing 22.37 defines a Profile property named firstName.

Listing 22.37. Web.Config

<?xml version="1.0"?> <configuration>   <system.web>   <profile>     <properties>       <add name="firstName" defaultValue="Steve" />     </properties>   </profile> </system.web> </configuration> 

The component in Listing 22.38 grabs the value of the firstName Profile property. Notice that the Profile object retrieved from the current HttpContext object must be case to a ProfileCommon object.

Listing 22.38. App_Code\ProfileComponent.vb

Imports System Imports System.Web Imports System.Web.Profile ''' <summary> ''' Retrieves first name from Profile ''' </summary> Public Class ProfileComponent     Public Shared Function GetFirstNameFromProfile() As String         Dim profile As ProfileCommon = CType(HttpContext.Current.Profile, ProfileCommon)         Return profile.firstName     End Function End Class 

Warning

To avoid conflicts with other code samples in this chapter, the component in Listing 22.38 is named ProfileComponent.vb_listing38 on the CD that accompanies this book. You'll need to rename the file to ProfileComponent.vb before you use the component.


Finally, the page in Listing 22.39 illustrates how you can call the ProfileComponent from within an ASP.NET page to retrieve and display the firstName attribute.

Listing 22.39. ShowProfileComponent.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Sub Page_Load()         lblFirstName.Text = ProfileComponent.GetFirstNameFromProfile()     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show Profile Component</title> </head> <body>     <form  runat="server">     <div>     First Name:     <asp:Label                  Runat="server" />     </div>     </form> </body> </html> 

Using the Profile Manager

Unlike Session state, profile data does not evaporate when a user leaves your application. Over time, as more users visit your application, the amount of data stored by the Profile object can become huge. If you allow anonymous profiles, the situation becomes even worse.

The ASP.NET Framework includes a class named the ProfileManager class that enables you to delete old profiles. This class supports the following methods:

  • DeleteInactiveProfiles Enables you to delete profiles that have not been used since a specified date.

  • DeleteProfile Enables you to delete a profile associated with a specified username.

  • DeleteProfiles Enables you to delete profiles that match an array of usernames or collection of ProfileInfo objects.

  • FindInactiveProfilesByUserName Enables you to retrieve all profiles associated with a specified username that have been inactive since a specified date.

  • FindProfilesByUserName Enables you to retrieve all profiles associated with a specified user.

  • GetAllInactiveProfiles Enables you to retrieve all profiles that have been inactive since a specified date.

  • GetAllProfiles Enables you to retrieve every profile.

  • GetNumberOfInactiveProfiles Enables you to retrieve a count of profiles that have been inactive since a specified date.

  • GetNumberOfProfiles Enables you to retrieve a count of the total number of profiles.

You can use the ProfileManager class from within a console application and execute the DeleteInactiveProfiles() method on a periodic basis to delete inactive profiles. Alternatively, you can create an administrative page in your web application that enables you to manage profile data.

The page in Listing 22.40 illustrates how you can use the ProfileManager class to remove inactive profiles (see Figure 22.10).

Figure 22.10. Deleting inactive profiles.


Listing 22.40. ManageProfiles.aspx

[View full width]

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Dim inactiveDate As DateTime = DateTime.Now.AddMonths(-3)     Private Sub Page_PreRender()         lblProfiles.Text = ProfileManager.GetNumberOfProfiles(ProfileAuthenticationOption .All).ToString()         lblInactiveProfiles.Text = ProfileManager.GetNumberOfInactiveProfiles (ProfileAuthenticationOption.All, inactiveDate).ToString()     End Sub     Protected Sub btnDelete_Click(ByVal sender As Object, ByVal e As EventArgs)         Dim results As Integer = ProfileManager.DeleteInactiveProfiles (ProfileAuthenticationOption.All, inactiveDate)         lblResults.Text = String.Format("{0} Profiles deleted!", results)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Manage Profiles</title> </head> <body>     <form  runat="server">     <div>     Total Profiles:     <asp:Label                  Runat="server" />     <br />     Inactive Profiles:     <asp:Label                  Runat="server" />     <br /><br />     <asp:Button                  Text="Delete Inactive Profiles"         Runat="server" OnClick="btnDelete_Click" />     <br />     <asp:Label                  EnableViewState="false"         Runat="server" />     </div>     </form> </body> </html> 

The page in Listing 22.40 displays the total number of profiles and the total number of inactive profiles. An inactive profile is a profile that has not been accessed for more than three months. The page also includes a Delete Inactive Profiles button that enables you to remove the old profiles.

Configuring the Profile Provider

By default, profile data is stored in a Microsoft SQL Server Express database named ASPNETDB.mdf, located in your application's root App_Data folder. If you want to store profile data in another database in your network, then you need to perform the following two tasks:

  • Add the necessary database objects required by the profile object to the database.

  • Configure your application to connect to the database.

You can add the necessary database tables and stored procedures required by the Profile object to a database by executing the aspnet_regsql command-line tool. The aspnet_regsql tool is located at the following path:

\WINDOWS\Microsoft.NET\Framework\[version]\aspnet_regsql.exe 


Note

If you open the SDK Command Prompt, then you do not need to navigate to the Microsoft.NET directory to execute the aspnet_regsql tool.


If you execute this tool without supplying any parameters, then the ASP.NET SQL Server Setup Wizard launches. This wizard guides you through the process of connecting to a database and adding the necessary database objects.

As an alternative to using the aspnet_regsql tool, you can install the necessary database objects by executing the following two SQL batch files:

\WINDOWS\Microsoft.NET\Framework\[version]\InstallCommon.sql \WINDOWS\Microsoft.NET\Framework\[version]\InstallProfile.sql 


After you have set up your database, you need to configure the default profile provider to connect to the database. The web configuration file in Listing 22.41 connects to a database named MyDatabase on a server named MyServer.

Listing 22.41. Web.Config

<?xml version="1.0"?> <configuration>   <connectionStrings>     <add       name="conProfile"       connectionString="Data Source=MyServer; Integrated Security=true;database=MyDatabase"/>   </connectionStrings>   <system.web>     <profile defaultProvider="MyProfileProvider">       <properties>         <add name="firstName" />         <add name="lastName" />       </properties>       <providers>         <add           name="MyProfileProvider"           type="System.Web.Profile.SqlProfileProvider"           connectionStringName="conProfile"/>       </providers>     </profile>   </system.web> </configuration> 

After you complete these configuration steps, all profile data is stored in a custom database.

Creating a Custom Profile Provider

The Profile object uses the Provider Model. The ASP.NET Framework includes a single profile provider, the SqlProfileProvider, that stores profile data in a Microsoft SQL Server database. In this section, you learn how to build a custom profile provider.

One problem with the default SqlProfileProvider is that it serializes an entire profile into a single blob and stores the blob in a database table column. This means that you can't execute SQL queries against the properties in a profile. In other words, the default SqlProfileProvider makes it extremely difficult to generate reports off the properties stored in a profile.

In this section, we create a new profile provider that is modestly named the BetterProfileProvider. The BetterProfileProvider stores each Profile property in a separate database column.

Unfortunately, the code for the BetterProfileProvider is too long to place in this book. However, the entire source code is included on the CD that accompanies this book.

The BetterProfileProvider inherits from the base ProfileProvider class. The two most important methods that must be overridden in the base ProfileProvider class are the GetPropertyValues() and SetPropertyValues() methods. These methods are responsible for loading and saving a profile for a particular user.

Imagine that you want to use the BetterProfileProvider to represent a profile that contains the following three properties: FirstName, LastName, and NumberOfVisits. Before you can use the BetterProfileProvider, you must create a database table that contains three columns that correspond to these Profile properties. In addition, the database table must contain an int column named ProfileID.

You can create the necessary database table with the following SQL command:

CREATE TABLE ProfileData {   ProfileID Int,   FirstName NVarChar(50),   LastName NVarChar(50),   NumberOfVisits Int } 


Next, you need to create a database table named Profiles. This table is used to describe the properties of each profile. You can create the Profiles table with the following SQL command:

CREATE TABLE Profiles (   UniqueID IDENTITY NOT NULL PRIMARY KEY,   UserName NVarchar(255) NOT NULL,   ApplicationName NVarchar(255) NOT NULL,   IsAnonymous BIT,   LastActivityDate DateTime,   LastUpdatedDate DateTime, ) 


After you create these two database tables, you are ready to use the BetterProfileProvider. The web configuration file in Listing 22.42 configures the BetterProfileProvider as the default profile provider.

Listing 22.42. Web.Config

<?xml version="1.0"?> <configuration>   <connectionStrings>     <add       name="conProfile"       connectionString="Data Source=.\SQLExpress; Integrated Security=true;AttachDBFileName=|DataDirectory|ProfilesDB.mdf;User Instance=true" />   </connectionStrings>   <system.web>     <profile defaultProvider="MyProfileProvider">       <properties>         <add name="FirstName" />         <add name="LastName" />         <add name="NumberOfVisits" type="Int32" />       </properties>       <providers>         <add           name="MyProfileProvider"           type="AspNetUnleashed.BetterProfileProvider"           connectionStringName="conProfile"           profileTableName="ProfileData" />       </providers>     </profile>   </system.web> </configuration> 

Notice that the BetterProfileProvider is configured with both a connectionStringName and profileTableName attribute. The connectionStringName points to the database that contains the two database tables that were created earlier. The profileTableName property contains the name of the table that contains the profile data. (This attribute defaults to the value ProfileData, so it really isn't necessary here.)

After you configure the BetterProfileProvider, you can use it in a similar manner to the default SqlProfileProvider. For example, the page in Listing 22.43 displays the values of the FirstName, LastName, and NumberOfVisits profile properties and enables you to modify the FirstName and LastName properties.

Warning

The BetterProfileProvider has several important limitations. It does not support serialization, so you cannot use it with complex types such as a custom shopping cart class. It also does not support default values for Profile properties.


Listing 22.43. ShowBetterProfileProvider.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server">     Sub Page_PreRender()         Profile.NumberOfVisits = Profile.NumberOfVisits + 1         lblNumberOfVisits.Text = Profile.NumberOfVisits.ToString()         lblFirstName.Text = Profile.FirstName         lblLastName.Text = Profile.LastName     End Sub     Sub btnUpdate_Click(ByVal sender As Object, ByVal e As EventArgs)         Profile.FirstName = txtNewFirstName.Text         Profile.LastName = txtNewLastName.Text     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>Show BetterProfileProvider</title> </head> <body>     <form  runat="server">     <div>     Number of Visits:     <asp:Label                  Runat="server" />     <br />     First Name:     <asp:Label                  Runat="server" />     <br />     Last Name:     <asp:Label                  Runat="server" />     <hr />     <asp:Label                  Text="First Name:"         AssociatedControl         Runat="server" />     <asp:TextBox                  Runat="server" />     <br />     <asp:Label                  Text="Last Name:"         AssociatedControl         Runat="server" />     <asp:TextBox                  Runat="server" />     <br />     <asp:Button                  Text="Update"         OnClick="btnUpdate_Click"         Runat="server" />     </div>     </form> </body> </html> 

The main advantage of the BetterProfileProvider is that you can perform SQL queries against the data stored in the ProfileData table. For example, the page in Listing 22.44 displays the contents of the ProfileData table in a GridView control (see Figure 22.11). You can't do this when using the default SqlProfileProvider because the SqlProfileProvider stores profile data in a blob.

Figure 22.11. Displaying a profile report.


Listing 22.44. BetterProfileProviderReport.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <title>BetterProfileProvider Report</title> </head> <body>     <form  runat="server">     <div>     <h1>Activity Report</h1>     <asp:GridView                  DataSource         Runat="server" />     <asp:SqlDataSource                  ConnectionString="<%$ ConnectionStrings:conProfile %>"         SelectCommand="SELECT ProfileID,FirstName,LastName,NumberOfVisits             FROM ProfileData"         Runat="server" />     </div>     </form> </body> </html> 




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net