ptg 1304 LISTING 28.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 28.23 includes the allowAnonymous attribute. The web configuration file also enables Forms authentication. When Forms authentication is enabled, and you don’t log in, you are an anonymous user. The page in Listing 28.24 illustrates how you modify a Profile property when Anonymous Identification is enabled. LISTING 28.24 ShowAnonymousIdentification.aspx <%@ Page Language=”C#” %> <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”> <script runat=”server”> void Page_PreRender() { lblUserName.Text = Profile.UserName; lblIsAnonymous.Text = Profile.IsAnonymous.ToString(); Profile.numberOfVisits++; lblNumberOfVisits.Text = Profile.numberOfVisits.ToString(); } CHAPTER 28 Maintaining Application State From the Library of Wow! eBook ptg 1305 Using Profiles 28 protected void btnLogin_Click(object sender, EventArgs e) { FormsAuthentication.SetAuthCookie(“Bob”, false); Response.Redirect(Request.Path); } protected void btnLogout_Click(object sender, EventArgs e) { FormsAuthentication.SignOut(); Response.Redirect(Request.Path); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > <head id=”Head1” runat=”server”> <title>Show Anonymous Identification</title> </head> <body> <form id=”form1” runat=”server”> <div> User Name: <asp:Label id=”lblUserName” Runat=”server” /> <br /> Is Anonymous: <asp:Label id=”lblIsAnonymous” Runat=”server” /> <br /> Number Of Visits: <asp:Label id=”lblNumberOfVisits” Runat=”server” /> <hr /> <asp:Button id=”btnReload” Text=”Reload” Runat=”server” /> <asp:Button id=”btnLogin” Text=”Login” OnClick=”btnLogin_Click” From the Library of Wow! eBook ptg 1306 Runat=”server” /> <asp:Button id=”btnLogout” Text=”Logout” OnClick=”btnLogout_Click” Runat=”server” /> </div> </form> </body> </html> Each time that you request the page in Listing 28.24, the numberOfVisits Profile prop- erty is incremented and displayed. The page includes three buttons: Reload, Login, and Logout (see Figure 28.8). CHAPTER 28 Maintaining Application State FIGURE 28.8 Creating an anonymous profile. The page also displays the value of the Profile.UserName property. This property repre- sents 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. From the Library of Wow! eBook ptg 1307 Using Profiles 28 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, the Profile.UserName property changes to the value Bob. The numberOfVisits property is reset. If you click the Logout button, 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, all the shopping cart items are lost. You can preserve the value of Profile properties when a user transitions from anony- mous 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 28.25 automatically copies the values of all anonymous Profile properties to the user’s current authenticated profile. LISTING 28.25 Global.asax <%@ Application Language=”C#” %> <script runat=”server”> public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs ➥ args) { // Get anonymous profile ProfileCommon anonProfile = Profile.GetProfile(args.AnonymousID); // Copy anonymous properties to authenticated foreach (SettingsProperty prop in ProfileBase.Properties) Profile[prop.Name] = anonProfile[prop.Name]; // Kill the anonymous profile ProfileManager.DeleteProfile(args.AnonymousID); AnonymousIdentificationModule.ClearAnonymousIdentifier(); } </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 From the Library of Wow! eBook ptg 1308 CHAPTER 28 Maintaining Application State anonymous identifier, 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 28.26 contains two properties named FirstName and LastName. LISTING 28.26 App_Code\SiteProfile.cs using System; using System.Web.Profile; public class SiteProfile : ProfileBase { private string _firstName = “Your First Name”; private string _lastName = “Your Last Name”; [SettingsAllowAnonymous(true)] public string FirstName { get { return _firstName; } set { _firstName = value; } } [SettingsAllowAnonymous(true)] public string LastName { get { return _lastName; } set { _lastName = value; } } } The class in Listing 28.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 28.27 uses the inherits attribute to inherit the Profile from the SiteProfile class. From the Library of Wow! eBook ptg 1309 Using Profiles 28 LISTING 28.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 downloadable code from the website that accompanies this book includes a page named ShowSiteProfile.aspx, which displays the Profile properties defined in Listing 28.27. NOTE If you inherit Profile properties from a class and define Profile properties in the web configuration file, 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 28.27 include the SettingsAllowAnonymous attribute, which allows anonymous users to read and modify the properties. From the Library of Wow! eBook ptg 1310 CHAPTER 28 Maintaining Application State Creating Complex Profile Properties To this point, we 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 28.28 represents a simple shopping cart. LISTING 28.28 App_Code\ShoppingCart.cs using System; using System.Collections.Generic; using System.Web.Profile; namespace AspNetUnleashed { public class ShoppingCart { private List<CartItem> _items = new List<CartItem>(); public List<CartItem> Items { get { return _items; } } } public class CartItem { private string _name; private decimal _price; private string _description; public string Name { get { return _name; } set { _name = value; } } public decimal Price { get { return _price; } set { _price = value; } } From the Library of Wow! eBook ptg 1311 Using Profiles 28 public string Description { get { return _description; } set { _description = value; } } public CartItem() { } public CartItem(string name, decimal price, string description) { _name = name; _price = price; _description = description; } } } The file in Listing 28.28 actually contains two classes: ShoppingCart and CartItem. The ShoppingCart class exposes a collection of CartItem objects. The web configuration file in Listing 28.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 28.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 28.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 28.9). From the Library of Wow! eBook ptg 1312 CHAPTER 28 Maintaining Application State LISTING 28.30 ShowShoppingCart.aspx <%@ Page Language=”C#” %> <%@ 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”> void Page_PreRender() { grdShoppingCart.DataSource = Profile.ShoppingCart.Items; grdShoppingCart.DataBind(); } protected void btnAdd_Click(object sender, EventArgs e) { CartItem newItem = new CartItem(txtName.Text, decimal.Parse(txtPrice.Text), ➥ txtDescription.Text); Profile.ShoppingCart.Items.Add(newItem); } </script> <html xmlns=”http://www.w3.org/1999/xhtml” > FIGURE 28.9 Storing a shopping cart in a profile. From the Library of Wow! eBook ptg 1313 Using Profiles 28 <head id=”Head1” runat=”server”> <title>Show ShoppingCart</title> </head> <body> <form id=”form1” runat=”server”> <div> <asp:GridView id=”grdShoppingCart” EmptyDataText=”There are no items in your shopping cart” Runat=”server” /> <br /> <fieldset> <legend>Add Product</legend> <asp:Label id=”lblName” Text=”Name:” AssociatedControlID=”txtName” Runat=”server” /> <br /> <asp:TextBox id=”txtName” Runat=”server” /> <br /><br /> <asp:Label id=”lblPrice” Text=”Price:” AssociatedControlID=”txtPrice” Runat=”server” /> <br /> <asp:TextBox id=”txtPrice” Runat=”server” /> <br /><br /> <asp:Label id=”lblDescription” Text=”Description:” AssociatedControlID=”txtDescription” Runat=”server” /> <br /> <asp:TextBox id=”txtDescription” Runat=”server” /> From the Library of Wow! eBook . anonymous properties to authenticated foreach (SettingsProperty prop in ProfileBase.Properties) Profile[prop.Name] = anonProfile[prop.Name]; // Kill the anonymous profile ProfileManager.DeleteProfile(args.AnonymousID);. Profile properties to represent more complex types such as a custom ShoppingCart class. For example, the class in Listing 28.28 represents a simple shopping cart. LISTING 28.28 App_CodeShoppingCart.cs using. Library of Wow! eBook ptg 1310 CHAPTER 28 Maintaining Application State Creating Complex Profile Properties To this point, we used the Profile properties to represent simple types such as strings