Evjen c15.tex V2 - 01/28/2008 2:44pm Page 747 Chapter 15: Personalization If you worked with the SQL Server personalization provider using SQL Server Express files as explained earlier, you probably found it easy to use. The personalization provider works right out of the box — without any set up or configuration on your part. Using the SQL Server personalization provider with a full-blown version of SQL Server, however, is a bit of a different story. Although it is not difficult to work with, you must set up and configure your SQL Server before using it. ASP.NET 3 .5 provides a couple o f ways to set up and configure SQL Server for the personalization framework. One way is through the ASP.NET SQL Server Setup Wizard, and the other method is by running some of the SQL Server scripts provided with the .NET Framework 2.0. Using the ASP.NET SQL Server Setup Wizard is covered in detail in Chapter 12. To use this wizard to set up your SQL Server for the ASP.NET 3.5 personalization features, you must first open up the aspnet_regsql.exe tool by invoking it from the Visual Studio 2008 Command Prompt. You open this command prompt by selecting Start ➪ All Programs ➪ Visual Studio 2008 ➪ Visual Studio Tools ➪ Visual Studio 2008 Command Prompt. At the prompt, type in aspnet_regsql.exe to open the GUI of the ASP.NET SQL Server Setup Wizard. If you step through the wizard, you can set up your SQL Server instance for many of the ASP.NET systems, such as the personalization system. Using SQL Scripts to Install Personalization Features Another option is to use the same SQL scripts that these tools and wizards use. If you look at C: \ WINDOWS \ Microsoft.NET \ Framework \ v2.0.50727 \, from this location, you can see the install and remove scripts — InstallPersonalization.sql and UninstallPersonalization.sql . R unning these scripts provides your database with the tables needed to run the personalization framework. Be forewarned that you must run the InstallCommon.sql script before running the personalization script (or any of the new other ASP.NET system scripts). Configuring the Provider for SQL Server 2005 After you have set up your SQL Server database for the personalization system, the next step is to redefine the personalization provider so that it works with this instance (instead of with the default Microsoft SQL Server Express Edition files). Youaccomplishthisinthe web.config file of your application. Here, you want to configure the provider and then define this provider instance as the provider to use. Listing 15-24 shows these additions plus the enlarged < profile > section of the web.config file. Listing 15-24: Connecting the SqlProfileProvider to S QL Server 2005 <configuration> <connectionStrings> <add name="LocalSql2005Server" connectionString="data source=127.0.0.1;Integrated Security=SSPI" /> </connectionStrings> <profile defaultProvider="AspNetSql2005ProfileProvider"> <providers> Continued 747 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 748 Chapter 15: Personalization <clear /> <add name="AspNetSql2005ProfileProvider" connectionStringName="LocalSql2005Server" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> </providers> <properties> <add name="FirstName" /> <add name="LastName" /> <add name="LastVisited" /> <add name="Age" /> <add name="Member" /> </properties> </profile> </configuration> The big change you make to this profile definition is to use the defaultProvider attribute with a value that is the name of the provider you want to use — in this case the newly created SQL Server provider, AspNetSql2005ProfileProvider . You can also make this change to the machine.config file by changing the < profile > element, as shown in Listing 15-25. Listing 15-25: Using SQL S erver as the provider in the machine.config file <configuration> <system.web> <profile enabled="true" defaultProvider="AspNetSql2005ProfileProvider"> </profile> </system.web> </configuration> This change forces each and every application that resides on this server to use this new SQL Server provider instead of the default SQL Server provider (unless this command is overridden in the applica- tion’s web.config file). Using Multiple Providers You are not limited to using a single data store or provider. Instead, you can use any number of providers. You can even specify the personalization provider for each property defined. This means that you can use the default provider for most properties, as well as allowing a few of them to use an entirely different provider (see Listing 15-26). 748 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 749 Chapter 15: Personalization Listing 15-26: Using different providers <configuration> <system.web> <profile defaultProvider="AspNetSqlProvider"> <properties> <add name="FirstName" /> <add name="LastName" /> <add name="LastVisited" /> <add name="Age" /> <add name="Member" provider="AspNetSql2005ProfileProvider" /> </properties> </profile> </system.web> </configuration> From this example, you can see that a default provider is specified — AspNetSqlProvider .Unless specified otherwise, this provider is used. The only property that changes this setting is the property Member .The Member property uses an entirely different personalization provider. In this case, it employs the Access provider ( AspNetSql2005ProfileProvider ) through the use of the provider attribute of the < add > element. With this attribute, you can define a specific provider for each and every property that is defined. Managing Application Profiles When you put into production an ASP.NET application that uses profile information, you quickly realize that you need a way to manage all the profile information collected over the lifecycle of the application. As you look at the ASP.NET MMC snap-in or t he ASP.NET Web Site Administration Tool, note that neither of these tools gives you a way to delete a specific user’s profile information or even to cleanse a database of profile information for users who haven’t been active in awhile. ASP.NET 3.5 gives you the means to manage the profile information that your application stores. This is done through the use of the ProfileManager class available in .NET. Through the use of the ProfileManager class, you can build-in the administration capabilities to completely manage the profile information that is stored by your application. In a ddition to being able to access property values, such as the name of the provider being used by the personalization system or the name of the application in question, you also have a large number of methods available in the Pro- fileManager class to retrieve all sorts of other information concerning your user’s profile. Through the ProfileManager class, you also have the capability to perform actions on this stored profile information including cleansing the database of old profile information. 749 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 750 Chapter 15: Personalization Properties of the ProfileManger Class The properties of the ProfileManager class are detailed in the following table. Properties Description ApplicationName Gets or sets the name of the application to work with. AutomaticSaveEnabled Gets or sets a Boolean value indicating whether the profile information is stored at the end of the page execution. Enabled Gets or sets a Boolean value indicating whether the application is able to use the personalization system. Provider Gets the name of the provider being used for the personalization system. Providers Gets a collection of all the providers available for the ASP.NET application. You can see that these properties include a bit of information about the personalization system and the providers available to it that you can integrate into any management system you build. Next, this chapter looks at the methods available for the ProfileManager class. Methods of the ProfileManager Class A good number of methods are available to the ProfileManager class that help you manage the profiles of the users of your application. These methods are briefly described in the following table. Properties Description DeleteInactiveProfiles Provides you with the capability to delete any profiles that haven’t seen any activity for a specified time period. DeleteProfile Provides you with the capability to delete a specific profile. DeleteProfiles Provides you with the capability to delete a collection of profiles. FindInactive Profiles ByUserName Provides you with all the inactive profiles under a specific username according to a specified date. FindProfilesBy UserName Provides you with all the profiles from a specific username. GetAllInactiveProfiles Provides you with all the profiles that have been inactive since a specified date. GetAllProfiles Provides you with a collection of all the profiles. GetNumberOf InactiveProfiles Provides you with the number of inactive profiles from a specified date. GetNumberOfProfiles Provides you with the number of total profiles in the system. As you can see from this list of methods, you can do plenty to manage to the profile information that is stored in your database. 750 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 751 Chapter 15: Personalization Next, this chapter looks at building a profile manager administration page for your ASP.NET application. This example builds it as an ASP.NET page, but you can just as easily build it as a console application. Building the ProfileManager.aspx Page To create a simple profile manager for your application, create a single ASP.NET page in your application called ProfileManager.aspx . You use this page to manage the profiles that are stored in the database for this particular application. This page includes a number of controls, but the most important is a DropDownList control that holds all the usernames of entities that have profile information in the database. You might see the same username a couple of times depending on what you are doing with your application. Remember that a single user can have multiple profiles in the database. Using the DropDownList control, you can select a user and see information about his profile stored in your data store. From this page, you can also delete his profile information. You can actually perform very many operations with the ProfileManager class, but this is a good example of some basic ones. The code for the ProfileManager.aspx page is presented in Listing 15-27. Listing 15-27: The ProfileManager.aspx page VB <%@ Page Language="VB" %> <script runat="server"> Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) If (DropDownList1.Items.Count = 0) Then WriteDropdownList() WriteUserOutput() End If End Sub Protected Sub DeleteButton_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) ProfileManager.DeleteProfile(DropDownList1.Text.ToString()) DropDownList1.Items.Clear() WriteDropdownList() WriteUserOutput() End Sub Protected Sub SelectButton_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) WriteUserOutput() End Sub Protected Sub WriteUserOutput() Dim outputInt As Integer Continued 751 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 752 Chapter 15: Personalization Dim pic As ProfileInfoCollection = New ProfileInfoCollection() pic = ProfileManager. FindProfilesByUserName(ProfileAuthenticationOption.All, _ DropDownList1.Text.ToString(), 0, 1, outputInt) DetailsView1.DataSource = pic DetailsView1.DataBind() End Sub Protected Sub WriteDropdownList() Dim outputInt As Integer Dim pic As ProfileInfoCollection = New ProfileInfoCollection() pic = ProfileManager.Provider. GetAllProfiles(ProfileAuthenticationOption.All, 0, 10000, outputInt) For Each proInfo As ProfileInfo In pic Dim li As ListItem = New ListItem() li.Text = proInfo.UserName.ToString() DropDownList1.Items.Add(li) Next Label1.Text = outputInt.ToString() End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <title>ProfileAdmin Page</title> </head> <body> <form id="form1" runat="server"> <div> <b>Profile Manager<br /> </b> <br /> Total number of users in system: <asp:Label ID="Label1" runat="server"></asp:Label><br /> <br /> <asp:DropDownList ID="DropDownList1" runat="server"> </asp:DropDownList> <asp:Button ID="SelectButton" runat="server" OnClick="SelectButton_Click" Text="Get User Profile Information" /><br /> <br /> <asp:DetailsView ID="DetailsView1" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None" Height="50px"> <FooterStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" /> <EditRowStyle BackColor="#7C6F57" /> <PagerStyle BackColor="#666666" ForeColor="White" HorizontalAlign="Center" /> <HeaderStyle BackColor="#1C5E55" Font-Bold="True" ForeColor="White" /> <AlternatingRowStyle BackColor="White" /> Continued 752 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 753 Chapter 15: Personalization <CommandRowStyle BackColor="#C5BBAF" Font-Bold="True" /> <RowStyle BackColor="#E3EAEB" /> <FieldHeaderStyle BackColor="#D0D0D0" Font-Bold="True" /> </asp:DetailsView> <br /> <asp:Button ID="DeleteButton" runat="server" Text="Delete Selected User’s Profile Information" OnClick="DeleteButton_Click" /> </div> </form> </body> </html> C# <%@ Page Language="C#" %> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if (DropDownList1.Items.Count == 0) { WriteDropdownList(); WriteUserOutput(); } } protected void DeleteButton_Click(object sender, EventArgs e) { ProfileManager.DeleteProfile(DropDownList1.Text.ToString()); DropDownList1.Items.Clear(); WriteDropdownList(); WriteUserOutput(); } protected void SelectButton_Click(object sender, EventArgs e) { WriteUserOutput(); } protected void WriteUserOutput() { int outputInt; ProfileInfoCollection pic = new ProfileInfoCollection(); pic = ProfileManager.FindProfilesByUserName (ProfileAuthenticationOption.All, DropDownList1.Text.ToString(), 0, 1, out outputInt); DetailsView1.DataSource = pic; DetailsView1.DataBind(); } protected void WriteDropdownList() Continued 753 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 754 Chapter 15: Personalization { int outputInt; ProfileInfoCollection pic = ProfileManager.Provider.GetAllProfiles (ProfileAuthenticationOption.All, 0, 10000, out outputInt); foreach (ProfileInfo proInfo in pic) { ListItem li = new ListItem(); li.Text = proInfo.UserName.ToString(); DropDownList1.Items.Add(li); } Label1.Text = outputInt.ToString(); } </script> Examining the Code of ProfileManager.aspx Page As you look over the code of the ProfileManager.aspx page, note that the ProfileManager class is used to perform a couple of different operations. First, the ProfileManager class’s GetAllProfiles() method is used to populate the DropDownList control that is on the page. The constructor of this method is presented here: GetAllProfiles( authenticationOption, pageIndex, pageSize, totalRecords) The GetAllProfiles() method takes a number of parameters, the first of which allows you to define whether you are using this method for all profiles in the system, or just the anonymous o r authenticated user’s profiles contained in the system. In the case of this example, all the profiles are retrieved with this method. This is accomplished using the ProfileAuthenticationOption enumeration. Then, the other parameters of the GetAllProfiles() method require you to specify a page index and the number of records to retrieve from the database. There is not a get all option (because of the potential size of the data that might be retrieved); so instead, in this example, I specify the first p age of data (using 0 ) and that this page contains the first 10,000 records (which is basically a get all for my application). The last parameter of the GetAllProfiles() method enables you to retrieve the count of the records if you want to use that anywhere within your application or if you want to use that number to iterate through the records. The ProfileManager.aspx page uses this number to display within the Label1 server control. In return from the GetAllProfiles() method, you get a ProfileInfoCollection object, which is a collection of ProfileInfo objects. Iterating through all the ProfileInfo objects in the ProfileIn- foCollection , you are able to pull out the some o f the main properties for a particular user’s profile information. In this example , just the UserName property of the ProfileInfo object is used to populate the DropDownList control on the page. When the end user selects one of the users from the dropdown list, the FindProfilesByUserName() method is used to display the profile of the selected user. Again, a ProfileInfoCollection object is returned from this method as well. 754 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 755 Chapter 15: Personalization To delete the profile of the user selected in the DropDownList control, simply use the DeleteProfile() method and pass in the name of the selected user as illustrated here: ProfileManager.DeleteProfile(DropDownList1.Text.ToString()) DropDownList1.Items.Clear() WriteDropdownList() WriteUserOutput() After the profile is deleted from the system, that name will not appear in the drop-down list anymore (because the DropDownList control has been redrawn). If you look in the database, particularly at the aspnet_Profile table, you see that the profile of the selected user is, in fact, deleted. However, also notice that the user (even if the user is anonymous) is still stored in the aspnet_Users table. If you want t o delete not only the profile information of the user but also delete the user from the aspnet_Users table, you should invoke the DeleteUser() method from the Membership class as presented here. ProfileManager.DeleteProfile(DropDownList1.Text.ToString()) Membership.DeleteUser(DropDownList1.Text.ToString()) DropDownList1.Items.Clear() WriteDropdownList() WriteUserOutput() This use of the DeleteUser() method also deletes the selected user from the aspnet_Users table. You could have also achieved the same thing by using the other constructor of the DeleteUser() method as presented here: Membership.DeleteUser(DropDownList1.Text.ToString(), True) DropDownList1.Items.Clear() WriteDropdownList() WriteUserOutput() The second parameter used in this operation of the DeleteUser() method deletes all data related to that user across all the tables held in the ASPNETDB.mdf database. Running the ProfileManager.aspx Page When you compile and run this page, you see results similar to those shown in Figure 15-6. From this screen, you can see that this page is dealing with an anonymous user (based upon the GUID for the username). You can also see that the IsAnonymous column is indeed checked. From this page, you can then delete this user’s profile information by selecting the appropriate button on the page. Summary The personalization capabilities provided by ASP.NET 3.5 make it incredibly easy to make your Web applications unique for all end users, whether they are authenticated or anonymous. This system enables you to store everything from basic data types provided by the .NET Framework to custom types that you create. This system is more versatile and extensible than using the Session or Application objects. The data is stored via a couple of built-in personalization providers that ship with ASP.NET. These providers 755 Evjen c15.tex V2 - 01/28/2008 2:44pm Page 756 Chapter 15: Personalization Figure 15-6 include ones that connect with either Microsoft’s SQL Server Express Edition files or Microsoft SQL Server 2008, 2005, 2000, or 7.0. You can also use the ProfileManager class to manage your system’s profile information. This includes the capability to monitor and delete profiles as you deem necessary. 756 . the ASP. NET 3. 5 personalization features, you must first open up the aspnet_regsql.exe tool by invoking it from the Visual Studio 2008 Command Prompt. You open this command prompt by selecting. the machine.config file by changing the < profile > element, as shown in Listing 15- 25. Listing 15- 25: Using SQL S erver as the provider in the machine.config file <configuration> <system.web> <profile. outputInt As Integer Continued 751 Evjen c 15. tex V2 - 01/28/2008 2:44pm Page 752 Chapter 15: Personalization Dim pic As ProfileInfoCollection = New ProfileInfoCollection() pic = ProfileManager. FindProfilesByUserName(ProfileAuthenticationOption.All,