O'Reilly logo

Visual C# 2005: A Developer's Notebook by Jesse Liberty

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Create Personalized Web Sites

Modern web sites that are designed to be visited by users repeatedly should support personalization for those users. Personalization enables the site to remember the user's preferences and, if appropriate, previous user choices (for example, "You have three items in your shopping cart.")

Tip

ASP.NET provides extensive support for personalization, allowing your site to "remember" your user's preferences.

How do I do that?

To get started, you'll want a new project that duplicates the work you accomplished in the previous lab. Here are the steps you need to take:

  1. Create a new web site and name it SitePersonalization.

  2. On the Visual Studio menu bar, choose Website Copy Website and click the Connect to Website button. The relevant part of the page is shown in Figure 4-28.

    Connecting to a remote site

    Figure 4-28. Connecting to a remote site

  3. Point to the previous lab and click Open. The wizard uses a question mark to identify the files that have the same name in both applications. Highlight all the files in the remote site, and then click the left-pointing arrow, as shown in Figure 4-29.

    Copying all files from the remote web site to the source web site

    Figure 4-29. Copying all files from the remote web site to the source web site

  4. Close the wizard and, if prompted, click Yes to overwrite files and Yes to update files.

    Tip

    If you did not create the previous lab, you can access the files by downloading the source code and copying it from the SecurityRoles folder.

  5. Run the program to make sure you have a duplicate of your previous lab.

The simplest form of personalization is to record information about the user, and then to make that information available whenever the user logs on. This requires a kind of persistence that goes beyond session state. To create true personalization, you'll want to store the user's choices and information in a database that associates the saved information with a particular user, and that persists indefinitely.

ASP.NET 2.0 provides all the plumbing required. You do not have to design, edit, or manage the database tables; all of that is done for you.

ASP.NET 2.0 has decoupled the Profile API (how you programmatically interact with profile data) from the underlying data provider (how you store the data). This allows you to use the default provider (SQL Server Express), or one of the other providers supplied (SQL Server), or even to write your own provider (for example, for an existing customer relationship management system), without changing the way you interact with the profile in the rest of your code.

To add data to the user's profile, first you must alert the system about the data you want to store. You do so in Web.config by adding a profile section to the system.web element:

<?xml version="1.0"?>
<configuration>
   <connectionStrings>
  <remove name="LocalSqlServer" />
  <add name="LocalSqlServer" connectionString="data source=.\sqlexpress;Integrated 
Security=SSPI;Initial Catalog=aspnetdb" />
 </connectionStrings>
 <system.web>
      <membership defaultProvider="AspNetSqlMembershipProvider" />
  <authentication mode="Forms"/>
      <roleManager enabled="True" defaultProvider="AspNetSqlRoleProvider" />
      <compilation debug="true"/>
        <profile>
               <properties>
               <add name="lastName" />
               <add name="firstName" />
               <add name="phoneNumber" />
               <add name="birthDate" type="System.DateTime"/>
               </properties>
               </profile>    
        
    </system.web>
</configuration>

This causes the Profile API to create storage for four pieces of information: first and last name, phone number, and birth date. The default storage type is string. Notice, however, that we are storing the birth date as a DateTime object.

You can gather this information in any way you want. To keep the example simple, we'll remove the role groups section from Default.aspx and add a new hyperlink to LoggedInTemplate:

<asp:LoginView ID="LoginView1" Runat="server">
    <LoggedInTemplate>
        Welcome
        <asp:LoginName ID="LoginName1" Runat="server" />
          <asp:HyperLink ID="linkProfile" Runat="server" 
                NavigateUrl="~/ProfileInfo.aspx">Add Profile Info
          </asp:HyperLink>
       ...
    </LoggedInTemplate>
    ...
</asp:LoginView>

As you can see, the link brings you to ProfileInfo.aspx, a page you'll create now. Add a table, and within the table add labels and checkboxes as well as a Save button, as shown in Figure 4-30.

The Profile table

Figure 4-30. The Profile table

The HTML code for the Profile table is shown in Example 4-3.

Example 4-3. HTML for the Profile table

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProfileInfo.aspx.cs" 
Inherits="ProfileInfo_aspx" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/
DTD/xhtml11.dtd">
    
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Profile Information</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <table>
            <tr>
                <td>First Name: </td>
                <td style="width: 193px">
                  <asp:TextBox ID="firstName" Runat="server" />
                </td>
            </tr>
            <tr>
                <td>Last Name: </td>
                <td style="width: 193px">
                 <asp:TextBox ID="lastName" Runat="server" /></td>
<!--            <td>
                   <asp:RequiredFieldValidator ID="lastNameRequired" Runat="server" 
ErrorMessage="Last name is required" ControlToValidate="lastName" Display="Dynamic">
                    *</asp:RequiredFieldValidator>
                </td> 
-->
            </tr>
            <tr>
                <td>Phone number: </td>
                <td style="width: 193px">
                   <asp:TextBox ID="phone" Runat="server" />
                </td>
            </tr>
            <tr>
                <td>BirthDate</td>
                <td style="width: 193px"><asp:TextBox ID="birthDate" 
Runat="server" /></td>
            </tr>
             <tr>
                <td>
                   <asp:Button ID="save" Text="Save" Runat="server" 
                       OnClick="save_Click" />
                 </td>
                <td style="width: 193px"></td>
            </tr>
        </table>
    </div>
        &nbsp;
    </form>
</body>
</html>

All you have to do now is add an event handler for the Save button:

void save_Click(object sender, EventArgs e)
{
   if (Profile.IsAnonymous =  = false)
   {
      Profile.lastName = this.lastName.Text;
      Profile.firstName = this.firstName.Text;
      Profile.phoneNumber = this.phone.Text;
      Profile.birthDate = Convert.ToDateTime(this.birthDate.Text);
   }
   Response.Redirect("Default.aspx");
}

When you start the application, you are asked to log in. Once you do this, a new hyperlink, Add Profile Info appears . This was created by the hyperlink you added to LoggedInTemplate (earlier). Clicking that link brings you to your new Profile page.

The Profile object has properties that correspond to the properties you added in Web.config. To test that the Profile object has in fact stored this data, you'll add a panel to the bottom of the default page, after the hyperlinks but before the closing </div> tag:

<asp:Panel ID="pnlInfo" Runat="server" Visible="False" Width="422px" Height="63px">
  <br />
  <table width="100%">
    <tr>
      <td>
        <asp:Label ID="lblFullName" Runat="server"  
         Text="Full name unknown">
        </asp:Label></td>
      </tr>
    <tr>
      <td>
        <asp:Label ID="lblPhone" Runat="server" 
          Text="Phone number unknown">
        </asp:Label>
      </td>
    </tr>
    <tr>
      <td>
        <asp:Label ID="lblBirthDate" Runat="server"  
            Text="Birthdate  unknown">
        </asp:Label>
      </td>
    </tr>
  </table>
 </asp:Panel>

The panel has a table with three rows, and each row has a label that is initialized to say that the value is unknown (this is not normally needed, but it's included here to ensure that the data you see was in fact retrieved from the Profile object). When the page is loaded, you check to see if you have profile data for this user and, if so, you assign that data to the appropriate controls:

public partial class Default_aspx
{
   public void Page_Load(object sender, EventArgs e)
   {
      if (Profile.UserName != null && Profile.IsAnonymous =  = false)
      {
         this.lblFullName.Text = "Full name: " +
             Profile.firstName + " " + Profile.lastName;
         this.lblPhone.Text = "Phone: " + Profile.phoneNumber;
         this.lblBirthDate.Text = "Born: " +
           Profile.birthDate.ToShortDateString( );
         this.pnlInfo.Visible = true;
      }
      else
      {
         this.pnlInfo.Visible = false;
      }
   }      // end page load
}         // end partial class

Notice that you convert DateTime to a string for easy display in the label.

Run the application, log in, and click Add Profile Info. You will be brought to the Profile Information form, as shown in Figure 4-31.

The Profile Information page

Figure 4-31. The Profile Information page

When you return to the default page, the Page_Load event fires, both parts of the if statement return true (that is, the UserName value in the profile is not null), and the user is logged in and thus is not anonymous:

if (Profile.UserName != null && Profile.IsAnonymous =  = false)

Your profile information is displayed, as shown in Figure 4-32.

Profile information displayed

Figure 4-32. Profile information displayed

What about...

...the profile information? Where is it stored?

To see where the profile information is stored, stop the application and examine the tables in your storage database. You'll want to examine two tables: aspnet_Users (which lists all the users your security system knows about) and aspnet_Profile (which lists the profile information for those users), as shown in Figure 4-33.

Examining the profile in the database

Figure 4-33. Examining the profile in the database

There are a number of things to notice. I've circled the UserID in both tables; the entries in the Profile table are matched to the individual user via the UserID.

The Profile table has two significant columns in addition to UserID: PropertyNames and PropertyValueString. The entries in the PropertyNames columns correspond to the entries you created in the <profile> section of Web.config:

<profile>
    <properties>
        <add name="lastName" />
        <add name="firstName" />
        <add name="phoneNumber" />
        <add name="birthDate" type="System.DateTime"/>
    </properties>
</profile>

Each property is named (for example, lastName) and is given a type (S for string), a starting offset (firstName begins at offset 7), and a length (firstName's value has a length of 5). This offset and value are used to find the value within the PropertyValueString field.

...what about saving complex types?

So far you've seen how to save built-in types such as strings and dates. In the next lab you'll see how to store complex types that might be needed to create a "shopping cart."

Note

Notice that birthDate is listed as a string that begins at offset 24 and is 95 characters long, but if you look at the PropertyValuesString column, you'll find that the birth date is encoded as XML.

Where can I learn more?

O'Reilly's ONDotnet site (http://www.ondotnet.com) provides numerous articles on personalization and extensive documentation on the subject is available in the MSDN.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required