Recipe 10.2. Using Profiles


Problem

You want to make use of user profile data throughout your application without having to implement the infrastructure yourself.

Solution

Use the ASP.NET 2.0 membership and profile features by implementing the membership features described in Recipe 9.5, modify web.config to enable user profiles, define in web.config the specific user data to include in the profile, provide the ability to update the user profile data, and use the data as required in your application.

Modify web.config as follows:

  1. Add a <profile> element with a <provider> element defining the provider used to store and retrieve user-specific data.

  2. Add a <properties> element and an <add> element for each user-specific data item to be included in the profile.

In the .aspx file for the page used to enter the user-specific data:

  1. Add a server control for each user-specific data item.

  2. Add an Update button (or equivalent) to initiate the updating of the user-specific data.

In the code-behind class for the page used to enter user-specific data, use the .NET language of your choice to:

  1. Initialize the controls in the .aspx file with the current data from the user's profile.

  2. Implement an event handler for the Update button click event and update the user's profile from the data entered by the user.

The solution we have implemented to demonstrate the solution is shown in Examples 10-1, 10-2, 10-3 through 10-4. Example 10-1 shows the modifications made to web.config. Example 10-2 shows the .aspx file used to enter the user-specific data, and Examples 10-3 (VB) and 10-4 (C#) show the code-behind classes for the page used to enter the user-specific data.

Discussion

With ASP.NET 1.x, as well as with classic ASP, no functionality was provided to maintain user profile information. If you needed the ability to maintain user profile information, you had to implement your own solution, which typically required a significant effort. With ASP.NET 2.0, the ability to maintain user profile information is provided by using the Profile features, and, in most cases, the only code required in your application is to use the information and to provide the ability for users to update their information.

Maintaining user profile information requires defining the data to be maintained, having the ability to store and retrieve the data, and having the ability to access the data. ASP.NET 2.0 simplifies each of these tasks.

The simplest way to define the data to include in the user profile is using web.config. By adding a <properties> element and <add> elements to the <profile> element for each user profile item to be stored, ASP.NET 2.0 dynamically creates a class containing the profile data. If you are using Visual Studio 2005, the class will be created in the background and its contents will be available using Intellisense, making use of the profile data even easier.

 <profile enabled="true" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" > … <properties>   <add name="FirstName"   type="System.String"   serializeAs="String"   allowAnonymous="false"   defaultValue=""   readOnly="false" />   <add name="LastName"   type="System.String"   serializeAs="String"   allowAnonymous="false"   defaultValue=""   readOnly="false" /> …   </properties> </profile> 

ASP.NET 2.0 includes one provider to handle the storage and retrieval of the user profile data, the SqlProfileProvider. This provider supports storing the user profile information in SQL Express, SQL Server 7.0, SQL Server 2000, or SQL Server 2005 without requiring you to write any code. All that is required is adding a <providers> element to the <profile> element in web.config. The attributes available for the SqlProfileProvider are shown in Table 10-1.

 <profile enabled="true" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" > <providers>     <remove name="AspNetSqlProfileProvider" /> <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="sqlConnectionString" applicationName="/" />   </providers>    … </profile> 

Table 10-1. SqlProfileProvider attributes

Attribute

Description

Name

Defines the name for the provider. Any value can be used.

Type

Specifies the class used to provide the functionality for the provider. Must be set to System.Web.Profile.SqlProfileProvider for the SqlProfileProvider.

connectionStringName

Specifies the name of the connection string used to access the SQL database used by the provider. Must be set to one of the connection strings defined in the <connectionStrings> element of web.config. Required for the SqlProfileProvider.

applicationName

Specifies the name of the application using the profile provider. The default value is the value of the System.Web.HttpRequest.ApplicationPath property.


The providers available to an application include all the providers defined in all the .config files in the application hierarchy. At a minimum, this will include machine.config and your application's web.config, but may also include additional web.config files if your application itself is hierarchical and has multiple web.config files. If a provider is included in multiple config files with the same name, an exception indicating that the provider exists will be thrown. To eliminate the possibility of accidental name collisions, include a <remove> element before the <add> element for the name of the provider you are adding. This is acceptable even if the provider does not exist since it is not an error to remove a nonexistent provider.


If you need to store the user profile information in a different data store, you can implement your own profile provider to store and retrieve the data. Refer to the ProfileProvider class in the MSDN Library for more information on implementing a custom profile provider.

In our application that implements this solution, we have implemented the Membership functionality for authentication and authorization, as described in Recipe 9.5, to provide the unique identification of the user for the profile data. The use of the Membership features in ASP.NET 2.0 is not required to use the Profile feature. All that is required is to provide a unique value for each user in the User.Identity.Name property of the Principal object used for authentication, which is used by the Profile Provider to identify the profile data required for the user. This means, for example, that the authentication solution provided in Recipe 9.1, which does not use Membership, can be used equally well.

The Membership and Profile providers share many tables in the database. If you want to use the Profile feature but do not intend to use the Membership features, you will or still need to add the tables required for the Membership and Profile providers. Refer to the "Using SQL Server Instead of SQLExpress with the Membership and Role Providers" sidebar in Recipe 9.5 for information on creating a database with the required tables or adding the required tables to an existing database.


The data to be included in the user profile is defined in web.config, as shown in Example 10-1. The definition of a data item (profile property) includes multiple attributes described in Table 10-2.

Table 10-2. Profile property attributes

Attribute

Description

name

Specifies the name of the property in the dynamically created Profile class. The name is required and must be unique.

type

Specifies the data type for the property. The type is required and can be any .NET data type or any custom data type of your choosing as long as the type can be serialized.

serializeAs

Specifies how the data is to be serialized for storage in the data store. Valid values are String, Xml, Binary, and ProviderSpecific. The default value is String.

allowAnonymous

Specifies if the property should be allowed for anonymous users. Valid values are true and false. The default value is false.

provider

Specifies the profile provider to be used to persist the property data. This provides the ability for individual properties to be persisted by different providers. The default value is the provider specified in the defaultProvider attribute of the <provider> element.

defaultValue

Specifies the default value used when no profile data is available for the property. This value is not stored in the data store by default. It is used to initialize the property in the Profile object only.

readOnly

Specifies if the property should be read-only. Valid values are TRue and false. The default value is false.


The data for our user profile uses a <group> element to group the properties for an address into a MailingAddress group. This approach makes using the data in your code easier in cases where you have multiple similar groups, such as a mailing address and a shipping address.

 <profile enabled="true"     defaultProvider="AspNetSqlProfileProvider"  automaticSaveEnabled="false" >    …     <properties>     <add name="FirstName"  type="System.String"  allowAnonymous="false"  provider="AspNetSqlProfileProvider"  defaultValue=""  readOnly="false" /> <add name="LastName"  type="System.String"  serializeAs="String"  allowAnonymous="false"  provider="AspNetSqlProfileProvider"  defaultValue=""  readOnly="false" /> <group name="MailingAddress">   <add name="Address1"    type="System.String"    serializeAs="String"    allowAnonymous="false"    provider="AspNetSqlProfileProvider"    defaultValue=""    readOnly="false" />   <add name="Address2"    type="System.String"    serializeAs="String"    allowAnonymous="false"    provider="AspNetSqlProfileProvider"    defaultValue=""    readOnly="false" />   <add name="City"    type="System.String"    serializeAs="String"    allowAnonymous="false"    provider="AspNetSqlProfileProvider"    defaultValue=""    readOnly="false" />   <add name="State"    type="System.String"    serializeAs="String"    allowAnonymous="false"    provider="AspNetSqlProfileProvider"    defaultValue=""    readOnly="false" />   <add name="ZipCode"    type="System.String"    serializeAs="String"    allowAnonymous="false"    provider="AspNetSqlProfileProvider"    defaultValue=""    readOnly="false" /> </group> <add name="EmailAddresses"  type="System.Collections.Specialized.StringCollection"  serializeAs="Xml"  allowAnonymous="false"  provider="AspNetSqlProfileProvider"  defaultValue=""  readOnly="false" />   </properties> </profile> 

To demonstrate how to handle collections of properties, the data in our application includes a collection of email addresses. The data type for this property is defined as StringCollection.

 <profile enabled="true"    defaultProvider="AspNetSqlProfileProvider"  automaticSaveEnabled="false" >    … <properties>    …    <add name="EmailAddresses" type="System.Collections.Specialized.StringCollection" serializeAs="Xml" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />    </properties>  </profile> 

When using collections, you cannot set the serializeAs property to String. When using the SqlProfileProvider, you should set it to Xml or Binary.


In the code-behind class for our application's demonstration page, we initialize the controls on the form with data from the logged in user's profile. For the simple properties, the Text properties for the Textbox controls are set with the profile values. For the email address collection, we first add a blank email address to the collection and then bind the data to a Repeater control containing Textbox controls. Adding the blank email address before binding renders an empty textbox to allow the user to enter an additional email address.

 

Private Sub initializeForm() txtFirstName.Text = Profile.FirstName txtLastName.Text = Profile.LastName txtAddress1.Text = Profile.MailingAddress.Address1 txtAddress2.Text = Profile.MailingAddress.Address2 txtCity.Text = Profile.MailingAddress.City txtState.Text = Profile.MailingAddress.State txtZipCode.Text = Profile.MailingAddress.ZipCode 'add a blank entry to allow the user to add a new email address Profile.EmailAddresses.Add("") repEmailAddresses.DataSource = Profile.EmailAddresses repEmailAddresses.DataBind() End Sub 'initializeForm

private void initializeForm() { txtFirstName.Text = Profile.FirstName; txtLastName.Text = Profile.LastName; txtAddress1.Text = Profile.MailingAddress.Address1; txtAddress2.Text = Profile.MailingAddress.Address2; txtCity.Text = Profile.MailingAddress.City; txtState.Text = Profile.MailingAddress.State; txtZipCode.Text = Profile.MailingAddress.ZipCode; // add a blank entry to allow the user to add a new email address Profile.EmailAddresses.Add(""); repEmailAddresses.DataSource = Profile.EmailAddresses; repEmailAddresses.DataBind(); } // initializeForm

When the user clicks the update button, we copy the data from the controls on the form to the user's profile, eliminating any blank email addresses in the process. We then call the Save method of the Profile object to update the user's profile data in the data store.

The <profile> element has an automaticSaveEnabled attribute that controls the automatic saving of the profile data. If the automaticSaveEnabled attribute is set to true, the profile data will be updated at the completion of a page request if any data has been changed. If the automaticSaveEnabled attribute is set to false, you will be responsible for performing the update.


You should not call the Save method of the Profile object if the automaticSaveEnabled attribute of the <profile> element is set to true. This can result in a performance impact since the profile data will be updated twice.


As this solution demonstrates, by using Profile features provided with ASP.NET 2.0, you can include user profile data in your application without writing any code to persist the data. In fact, with the provider model used by ASP.NET, your application does not require any knowledge of how the profile data is persisted. With the profile API provided, you can change the profile provider to use a different data store without making any changes to your application code.

See Also

Recipes 9.1, 9.5, 10.2, and 10.3; MSDN Library for information on implementing a custom profile provider

Example 10-1. Modifications to web.config for user profiles

 <?xml version="1.0"?> <configuration>  <system.web>    <!-- The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user.  -->    <authentication mode="Forms" >   <forms name=".ASPNETCookbookVBPersonalization1" loginUrl="Login.aspx" protection="All" timeout="30" path="/" defaultUrl="UpdateProfile.aspx" /> </authentication> <authorization>   <deny users="*" /> <!-- Deny all users --> </authorization> <membership defaultProvider="AspNetSqlMembershipProvider"    userIsOnlineTimeWindow="15">   <providers> <remove name="AspNetSqlMembershipProvider" /> <add name="AspNetSqlMembershipProvider"        type="System.Web.Security.SqlMembershipProvider" connectionStringName="sqlConnectionString" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false"     minRequiredPasswordLength="4" minRequiredNonalphanumericCharacters="0" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" passwordAttemptWindow="10" passwordStrengthRegularExpression="" />   </providers>     </membership>       <roleManager defaultProvider="AspNetSqlRoleProvider"       enabled="true"       cacheRolesInCookie="true"       cookieName=".ASPNETCookbookVBPersonalization1Roles"       cookieTimeout="30"       cookiePath="/"       cookieRequireSSL="false"       cookieSlidingExpiration="true"       cookieProtection="All" >   <providers> <remove name="AspNetSqlRoleProvider" /> <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="sqlConnectionString" applicationName="/" />   </providers> </roleManager> ><profile enabled="true" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" >   <providers>     <remove name="AspNetSqlProfileProvider" /> <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="sqlConnectionString" applicationName="/" />   </providers>   <properties>     <add name="FirstName" type="System.String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> <add name="LastName" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> <group name="MailingAddress">   <add name="Address1" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />   <add name="Address2" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />   <add name="City" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />   <add name="State" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />   <add name="ZipCode" type="System.String" serializeAs="String" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" />   </group>   <add name="EmailAddresses" type="System.Collections.Specialized.StringCollection" serializeAs="Xml" allowAnonymous="false" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> </properties> </profile>   </system.web>   <!-   **************************************************************************** The following section defines the pages that require authentication for access. An entry must be included for each page that requires authentication with a list of the roles required for access to the page. Valid Roles are as follows. NOTE: The roles must be entered exactly as listed. User Admin   ****************************************************************************   -->   <location path="UpdateProfile.aspx"> <system.web> <authorization>       <allow roles="User,    Admin"/>    <deny users="*"/> </authorization> </system.web>   </location> </configuration> 

Example 10-2. Update user profile data (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="UpdateProfile.aspx.vb" Inherits="ASPNetCookbook.VBExamples.UpdateProfile" Title="User Profile" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" > User Profile (VB) </div> <table width="60%" align="center" border="0"> <tr>   <td >FirstName: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>   <td >LastName: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>       <td >Address 1: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>       <td >Address 2: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>       <td >City: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>       <td >State: </td>   <td><asp:TextBox  runat="server" /></td>     </tr>     <tr>       <td >Zip Code: </td>   <td><asp:TextBox  runat="server" /></td>   </tr>   <tr>       <td valign="top" >Email Addresses: </td>   <td>     <table border="0" cellpadding="0" cellspacing="0">       <asp:Repeater  runat="server">     <ItemTemplate>       <tr>     <td>       <asp:TextBox  runat="server"       Text="<%#Container.DataItem%>" />     </td>   </tr>     </ItemTemplate>   </asp:Repeater>     </table>   </td>     </tr>     <tr>       <td colspan="2" align="center">     <br />     <asp:Button  runat="server" Text="Update" OnClick="btnUpdate_Click" />      </td>   </tr> </table> </asp:Content> 

Example 10-3. Update user profile data (.vb)

 Option Explicit On Option Strict On Imports System Imports System.Configuration.ConfigurationManager Imports System.Data Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples   ''' <summary>   ''' This class provides the code behind for   ''' UpdateProfile.aspx   ''' </summary>   Partial Class UpdateProfile Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _   ByVal e As System.EventArgs) Handles Me.Load If (Not Page.IsPostBack) Then     initializeForm( )     End If End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the update button click ''' event. It is responsible for updating the user profile from the data ''' on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub btnUpdate_Click(ByVal sender As Object, _   ByVal e As System.EventArgs) Dim item As RepeaterItem Dim txtBox As TextBox Dim emailAddress As String Dim emailAddresses As StringCollection Profile.FirstName = txtFirstName.Text Profile.LastName = txtLastName.Text Profile.MailingAddress.Address1 = txtAddress1.Text Profile.MailingAddress.Address2 = txtAddress2.Text Profile.MailingAddress.City = txtCity.Text Profile.MailingAddress.State = txtState.Text Profile.MailingAddress.ZipCode = txtZipCode.Text emailAddresses = New StringCollection For Each item In repEmailAddresses.Items   txtBox = CType(item.FindControl("txtEmailAddress"), _      TextBox)   emailAddress = txtBox.Text.Trim   If (emailAddress.Length > 0) Then emailAddresses.Add(emailAddress)   End If Next item Profile.EmailAddresses = emailAddresses Profile.Save( ) initializeForm( ) End Sub 'btnUpdate_Click '''*********************************************************************** ''' <summary> ''' This routine updates the form with data from the user's profile ''' </summary> Private Sub initializeForm( ) txtFirstName.Text = Profile.FirstName txtLastName.Text = Profile.LastName txtAddress1.Text = Profile.MailingAddress.Address1 txtAddress2.Text = Profile.MailingAddress.Address2 txtCity.Text = Profile.MailingAddress.City txtState.Text = Profile.MailingAddress.State txtZipCode.Text = Profile.MailingAddress.ZipCode 'add a blank entry to allow the user to add a new email address Profile.EmailAddresses.Add("") repEmailAddresses.DataSource = Profile.EmailAddresses repEmailAddresses.DataBind( ) End Sub 'initializeForm   End Class 'UpdateProfile End Namespace 

Example 10-4. Update user profile data (.cs)

 using System; using System.Web.UI.WebControls; using System.Collections.Specialized; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code behind for /// UpdateProfile.aspx /// </summary> public partial class UpdateProfile : System.Web.UI.Page {    ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) {   if (!Page.IsPostBack)   {     initializeForm( );   } } // Page_Load ///*********************************************************************** /// <summary> /// This routine provides the event handler for the update button click /// event. It is responsible for updating the user profile from the data /// on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void btnUpdate_Click(Object sender, System.EventArgs e) {   TextBox txtBox;   String emailAddress;   StringCollection emailAddresses;   Profile.FirstName = txtFirstName.Text;   Profile.LastName = txtLastName.Text;   Profile.MailingAddress.Address1 = txtAddress1.Text;   Profile.MailingAddress.Address2 = txtAddress2.Text;   Profile.MailingAddress.City = txtCity.Text;   Profile.MailingAddress.State = txtState.Text;   Profile.MailingAddress.ZipCode = txtZipCode.Text;   emailAddresses = new StringCollection( );   foreach (RepeaterItem item in repEmailAddresses.Items)   {     txtBox = (TextBox)(item.FindControl("txtEmailAddress")); emailAddress = txtBox.Text.Trim( ); if (emailAddress.Length > 0) {   emailAddresses.Add(emailAddress); }   }   Profile.EmailAddresses = emailAddresses;   Profile.Save( );   initializeForm( ); }  // btnUpdate_Click ///*********************************************************************** /// <summary> /// This routine updates the form with data from the user's profile /// </summary> private void initializeForm( ) {   txtFirstName.Text = Profile.FirstName;   txtLastName.Text = Profile.LastName;   txtAddress1.Text = Profile.MailingAddress.Address1;   txtAddress2.Text = Profile.MailingAddress.Address2;   txtCity.Text = Profile.MailingAddress.City;   txtState.Text = Profile.MailingAddress.State;   txtZipCode.Text = Profile.MailingAddress.ZipCode;   // add a blank entry to allow the user to add a new email address   Profile.EmailAddresses.Add("");   repEmailAddresses.DataSource = Profile.EmailAddresses;   repEmailAddresses.DataBind( ); } // initializeForm   } // UpdateProfile } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

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