It is becoming harder and harder to find web applications that don't store information in user profiles. Information such as a user's name, e-mail address, phone number, and even credit card information is stored by so many websites that most users are pretty used to filling out profiles and new-user forms on websites. In the past, every time you needed an application that maintained user profile information, you had to create a database (or at least a few tables in an existing database) to store that information and probably a library of classes that encapsulated the interaction with the back-end profile store. That task has been made obsolete in ASP.NET 2.0. This section of the chapter shows you how to configure application services (used to store profiles and much more, such as membership data), how to use an ASP.NET profile provider, and finally how to write code that allows the storage and retrieval of user profile data. Configure Application ServicesTo store profile information in an underlying database, you need to have the database created and the appropriate tables and stored procedures created. Fortunately, ASP.NET provides a wizard that walks you through the process of creating the application services database. Application services consist of membership data, profile data, personalization data (discussed in Chapter 26, "Introduction to Web Parts"), role data, and event data. This allows you to create one application services database, and it will take care of many of the tasks that used to require manual development effort in previous versions of ASP.NET. To create a new database or configure an existing one for application services, you need to use the ASP.NET SQL Server Setup Wizard that ships with ASP.NET. It is a command-line tool called aspnet_regsql. This tool will automatically be in your path if you open up a Visual Studio 2005 Command Prompt (in the Visual Studio Tools subfolder under Visual Studio 2005 in your Start menu). When you execute aspnet_regsql with no parameters, you will first be prompted with a screen indicating the application's purpose. When you click Next, you will see the screen displayed in Figure 25.3. Figure 25.3. ASP.NET SQL Server Configuration Wizard.You will be prompted for the name of the SQL Server in which the application services database will be created, as well as for credentials (or you can use default Windows credentials). Keep in mind that all you're doing at this stage is creating the database; none of the information you enter here will be recorded in any ASP.NET application. After you follow all the directions in the ASP.NET SQL Server Configuration Wizard, you can go into your SQL Server Management Studio and look at the contents of the database you just created. It doesn't have to make much sense because the ASP.NET providers expose a very robust API for interacting with that data. Configuring the Profile ProviderWhen the application services database has been created and is ready for use, you will need to create an application that utilizes that database for its profile provider. Using Application Services It might get confusing when you use application services because of the Provider model. There are multiple ASP.NET providers and a SQL provider for membership, roles, profiles, and more. When you create an application services database, it creates tables and stored procedures that can support all of the ASP.NET SQL providers. In this fashion, you can create a single connection string to your application services database and reuse that connection string for all of your providers. To configure your application to use a specific profile provider, you need to use the <profile> element and the <connectionStrings> element of the Web.config file. Create a new ASP.NET application called UserProfiles and leave all of the default content in place. Add a Web.config file to the project. The first step to configuring an application to use a profile provider is to create a connection string that points at the application services database, as shown in the following code:
This creates a connection string that will establish a connection to the application services database created during the wizard (the one in the example is called ASPNET_AppServicesyou will have to replace the database name with the name of the one you created). This connection string follows all the rules of standard ADO.NET SQL connection strings. The next step is to configure the profile provider by indicating which connection string the profile provider will use to connect to its data. You also need to specify the type of the profile provider. In this case, we'll be using the System.Web.Profile.SqlProfileProvider. The <profile> section (contained within <system.web>) looks like the one shown in the following code: <profile defaultProvider="AppServicesProvider"> <providers> <add name="AppServicesProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="appSvcs"/> </providers> <properties> <add name="FirstName" type="string" allowAnonymous="true"/> </properties> </profile> The <profile> element indicates which provider defined within the subelement <providers> will be used as the default provider. The <properties> element will be discussed in the "Using ASP.NET Profiles" section. If you run your application as it is now with just the default content and the modified Web.config, you will get a runtime error if the connection to the profile data source failed, and your application will run just fine if the profile data source connection worked properly. Using ASP.NET ProfilesAt its very core, a user profile is really nothing more than a collection of name-value pairs. For example, a sample user profile might contain the following name-value pairs:
If you create a database table that contains user profile data and just the columns from the preceding list, you could write code that reads and writes user profiles from that table. The problem is that user profiles tend to grow over time, and you may need to add or remove columns at some point in your application's life. If you have a fixed set of columns in that table, this kind of maintenance can become a nightmare. The ASP.NET profile providers fix this problem by allowing the properties that belong to a user's profile to be arbitrary. You can define the list of properties that you want to manage for your users directly within your Web.config file. The profile provider takes care of the details of persisting and retrieving the data, leaving you with a very simple API for managing user profile data: the Profile object. The Profile object is available to all web pages, and inherits from System.Web.Profile.ProfileBase. Table 25.1 lists the properties that come with the base profile class.
In addition to the stock properties defined by the ProfileBase class, ASP.NET actually adds a property to the Profile object for every property you have defined in Web.config. For example, if you have defined the FirstName property in Web.config (see the <profile> element from the preceding section), you can write code that looks like this: lblFirstName.Text = Profile.FirstName; Profile.FirstName = txtFirstName.Text; This is extremely powerful because even at design time, you have a strongly typed, IntelliSense-aware set of properties that are read from Web.config. Defining a property in the <properties> subelement of the <profile> element is fairly easy. You use the following format: <add name="FirstName" type="string" allowAnonymous="true"/> The name of the property is self-explanatory. This name will also be used as a property name on the dynamically constructed Profile object. The type attribute indicates the data type. You can actually use any .NET data type, even one you have created: <add name=""ShoppingCart"" type=""MyApp.Utilities.ShoppingCart"" /> The only requirement on the data type of the profile property is that it must be a type that can be resolved from the ASP.NET project, so the type needs to be either in the App_Code directory of the application or in an assembly that is referenced by the project. Note If you are planning on using a custom-created type as a profile property, that type must be serializable. You should follow the same rules for out-of-process session-state data when determining what to store in a user's profile: It must be consistently serializable. When you serialize and then deserialize the object, it should represent exactly the same data. There should be no "side-effect" code in any property accessors, and it should have an empty default constructor. The allowAnonymous attribute indicates whether an anonymous profile (a profile owned by an unauthenticated user) should have that property. In such situations, you might choose to allow an anonymous user to have a shopping cart and then migrate their anonymous profile to a named profile as they create an account during the checkout process. When they have a named profile, they can have additional properties such as a name, address, billing and shipping information, and so on. To illustrate how to use profiles and profile properties, let's get started with a full Web.config that stores the user's first and last names as well as the number of times that the user has hit the home page. Listing 25.1 contains a complete Web.config using the previously configured application services database and three profile properties, all of which are allowed by anonymous users. Listing 25.1. A Web.config Supporting Profile Properties
Now add a page to your application called MyProfile.aspx. This page will be responsible for allowing the users to edit their first and last names, as well as displaying the number of times they have accessed the home page of the application. The source code for MyProfile.aspx.cs is shown in Listing 25.2. Listing 25.2. MyProfile.aspx.cs
The code is pretty straightforward. If the page is not a postback, the user's profile information is loaded into the appropriate text box and label controls. After the user clicks Save, the user's profile information is modified according to the data contained in the two text boxes, and the Profile.Save() method is called to ensure the data is stored in the database. Any developers who have had to code their own profile storage system can appreciate how incredibly powerful it is to now have design-time, strongly typed profile properties and to have the complexity of storing and retrieving those profiles handled automatically by ASP.NET's SQL Profile Provider. The only remaining piece of work that needs to be done to create a full-featured application using profiles is to migrate the anonymous profile to a real profile when the anonymous user becomes authenticated. To handle this situation, just add the following method to the Global.asax.cs file: public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args) { // locate the anonymous profile being migrated ProfileCommon anonProfile = Profile.GetProfile(args.AnonymousID); // set new profile data to data from the ''old'' anon profile Profile.FirstName = anonProfile.FirstName; Profile.LastName = anonProfile.LastName; Profile.HomeHits = anonProfile.HomeHits; Profile.Save(); // remove the anon profile and the associated cookie ProfileManager.DeleteProfile(args.AnonymousID); AnonymousIdentificationModule.ClearAnonymousIdentifier(); } Because an anonymous profile can only be migrated to a named profile when the user goes from being anonymous to being authenticated, you will probably not run into this situation until you start using the Membership provider that is discussed in Chapter 28, "Securing Your ASP.NET Applications." Keep this code in mind, and when you're done with both this chapter and Chapter 28, you can try to create an application that uses both Membership and Profile providers not only to authenticate users, but also to store their profile information. |