作者: Stephen Walther
原文地址:http://msdn.microsoft.com/asp.net/default.aspx?pull=/library/en-us/dnvs05/html/UserProfiles.asp
譯
者:Tony Qu
概要:許多ASP.NET應用程序需要跨訪問的用戶屬性 跟蹤功能,在ASP.NET1.1中,我們只能人工實現這一功能。但如今,使用 ASP.NET 2.0的Profile對象,這個過程變得異 常簡單。Stephen Walther將驗證該對象,並向你展示如何使用Profile來跟蹤用戶屬性、創建一個購物籃,及其他一些例子。
總目錄
介紹
User Profile總攬
定義User Profile
使用Profile組
使用複雜的Profile屬性
繼承一個Profile
遷移匿名Profile設置
配置Profile Provider
管理Profiles並生成Profile報告
總結
相關書籍
Microsoft
ASP.NET 2.0支持被稱爲Profile的新對象,它可以自動在多個Web應用程序的訪問之間存儲用戶信息。一個User Profile
中可以存儲各種類型的信息,這些信息既可以是簡單的string和integer類型,也可以是複雜的自定義類型。例如,你可以存儲用戶的姓、購物籃、用
戶屬性或網站使用情況統計。
本文中,你將學習如何在一個應用中定義user profile。我們也會向你演示如何配置使用不同
provider的profile。最後,你將學習如何管理和生成user profile的報告。
User Profiles總攬
Profile
對象與Session對象十分相似,但是更好用一些。與Session相似的地方在於,Profile是相對於一個特定的用戶的,也就是說,每
個Web應用程序的用戶都有他們自己的profile對象。與Session不同的是,Profile對象是持久對象。如果你向
Session中添加一個項,在你離開網站時,該項就會消失。而Profile則完全不同,當你修改Profile的狀態時,修改在
多個訪問之間均有效。
profile使用provider模式來存儲信息,默認情況下,user profile 的內容會保存在SQL Server Express數據庫中,該數據庫位於網站的App_Data目錄。然而,在本文的後半部分,你將瞭解如 何使用其他數據提供者(data provider)來存儲信息,如完整版的SQL Server中的一個數據庫或者一個Oracle數據庫。
與Session不同,Profile是強類型的,Session對象僅僅是一個項集合而已,而profile對象則有強類
型屬性。
使用強類型是有它的道理的。例如,使用強類型,你就可以在Microsoft Visual Web
Developer中使用智能感知技術,當你鍵入Profile和一個點的時候,智能感知會彈出你已經定義過的profile屬性列
表。
定義user
profile
你既可以在machine.config中,也可以在web.config中定義一個user profile,
由於你不能在應用程序的二級目錄中創建一個包含文件profile節的web.config文件,這意味着你將無法在一個應用程序中定義兩個以
上的profile。
在列表1的web.config中,列舉了一個簡單的profile定義的實例,該profile
有三個屬性,FirstName, LastName和PageVisits。
< configuration >
< system .web >
< authentication mode ="Forms" />
< anonymousIdentification enabled ="true" />
< profile >
< properties >
< add
name ="FirstName"
defaultValue ="??"
allowAnonymous ="true" />
< add
name ="LastName"
defaultValue ="??"
allowAnonymous ="true" />
< add
name ="PageVisits"
type ="Int32"
allowAnonymous ="true" />
</ properties >
</ profile >
</ system.web >
</ configuration >
由於該profile需要同時被匿名用戶和已認證用戶使用,因此我們在
web.config文件中增加包含一個
<
anonymousIdentification>元素,有了這個元素,系統就會自動爲匿名用戶生成唯一的ID。仔細看的話我們會發現,每一個
pro
默認的profile屬性類型是System.String類型。列表1中,由於沒有爲FirstName 和LastName這兩個profile屬性增加type特性,那麼系統默認它們是string類型,而PageVisits屬性則指定了 type特性爲Int32,因此該profile屬性可用於表示一個整型值。
最後,注意FirstName和LastName屬性都有defaultValue特性。你可以爲簡單的數據類型設置defaultValue特性,但你
不能爲複雜類型設置defaultValue特性。
當你定義好一個profile之後,系統會自動在下一次頁面被調
用時,生成一個與該profile相對應的類。這個類會被保存在"Temporary ASP.NET Files
Directory"目錄(該目錄也用於存放用於動態生成頁面的類)。你可以使用HttpContext的Profile屬性
(Property)調用該類。
當你定義好一個profile後,你可以使用如下方法爲profile屬性賦值。
Profile.FirstName = " Bill "
[C#]
Profile.FirstName = " Bill " ;
任何在web.config中定義的profile
屬性都會在Profile對象中呈現。
列表2演示了你該如何使用profile來持久化保存用戶信息。這個頁顯示了
FirstName,LastName,
PageVisits三個屬性的值,同時它包含了一個能夠用於修改這三個屬性的表單(form)。在Page_Load中更新PageVisits的值,
這意味着每一次刷新頁面,PageVisits的值都會改變。
圖1 使用簡單的profile
< %@ Page Language = " VB " % >
< script runat = " server " >
Sub Page_Load()
Profile.PageVisits += 1
End Sub
Sub UpdateProfile( ByVal s As Object , ByVal e As EventArgs)
Profile.FirstName = txtFirstName.Text
Profile.LastName = txtLastName.Text
End Sub
</ script >
< html >
< head >
< title > Simple </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< b > Name: </ b > < % = Profile.FirstName % > < % = Profile.LastName % >
< br />
< b > Page Visits: </ b > < % = Profile.PageVisits % >
< hr />
< b > First Name: </ b >
< asp:TextBox ID = " txtFirstName " Runat = " Server " />
< br />
< b > Last Name: </ b >
< asp:TextBox ID = " txtLastName " Runat = " Server " />
< br />
< asp:Button
Text = " Update Profile "
OnClick = " UpdateProfile "
Runat = " server " />
</ form >
</ body >
</ html >
<% @ Page Language = " C# " %>
< script runat = " server " >
void Page_Load() {
Profile.PageVisits ++ ;
}
void UpdateProfile(Object s, EventArgs e) {
Profile.FirstName = txtFirstName.Text;
Profile.LastName = txtLastName.Text;
}
</ script >
< html >
< head >
< title > Simple </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< b > Name: </ b > <%= Profile.FirstName %> <%= Profile.LastName %>
< br />
< b > Page Visits: </ b > <%= Profile.PageVisits %>
< hr />
< b > First Name: </ b >
< asp:TextBox ID = " txtFirstName " Runat = " Server " />
< br />
< b > Last Name: </ b >
< asp:TextBox ID = " txtLastName " Runat = " Server " />
< br />
< asp:Button ID = " Button1 "
Text = " Update Profile "
OnClick = " UpdateProfile "
Runat = " server " />
</ form >
</ body >
</ html >
如果你多次訪問列表2中的頁面,你會注意到PageVisits在不斷增大。如果你關閉的瀏覽器,並
在一週之後調用該頁面,PageVisits屬性仍然會保留原值。從這一點可以看出Profile爲每個用戶自動保存一個副本。
使用Profile組
儘管你僅可以爲一個應用程序定義一個 profile ,但如果你需要讓幾個 profile 屬性一起工作,把它們放在組中,會讓你覺得它們更易管理。
例如,在列表 3 中,有一個帶有兩個組的 profile ,這兩個組分別是 Address 和 Preferences
< configuration >
< system .web >
< anonymousIdentification enabled ="true" />
< profile >
< properties >
< group name ="Address" >
< add
name ="Street"
allowAnonymous ="true" />
< add
name ="City"
allowAnonymous ="true" />
</ group >
< group name ="Preferences" >
< add
name ="ReceiveNewsletter"
type ="Boolean"
defaultValue ="false"
allowAnonymous ="true" />
</ group >
</ properties >
</ profile >
</ system.web >
</ configuration >
當你用組來定義 profile 時,你應該使用組名來設置或讀取 profile 屬性。例如,在列表 3 中,你可以使用以下一些句子來完成三個 profile 屬性的賦值。
Profile.Address.City = " Modesto "
Profile.Address.Street = " 111 King Arthur Ln "
Profile.Preferences.ReceiveNewsletter = False
[C#]
Profile.Address.City = " Modesto " ;
Profile.Address.Street = " 111 King Arthur Ln " ;
Profile.Preferences.ReceiveNewsletter = false ;
一個 profile 的定義只能包含一 層組,換句話說,你不能把其他的組放在一個 profile 組的下面一層。
使用複雜的profile屬 性
到目前爲止,我們已經介紹了聲明包含簡單類型(如string或整型)屬性的profile,其實你也可以在profile
中聲明覆雜屬性。
舉個例子,假設你現在需要在profile中存儲一個購物籃,如果這樣做的話,你就可以在每次訪問網站時獲得自己的購
物籃。
列表4 聲明瞭一個包含profile,這個profile包含一個名爲ShoppingCart的屬性,而該屬性的
type特性是一個叫ShoppingCart的類(我們接下來會創建該類),該類名是有效的。
我們還會注意到,該聲明中包含一個
serializeAs特性,該特性可以幫助ShoppingCart使用二進制序列化器(binary
serializer)進行持久化,而不是使用xml序列化器。
< configuration >
< system .web >
< anonymousIdentification enabled ="true" />
< profile >
< properties >
< add
name ="ShoppingCart"
type ="ShoppingCart"
serializeAs ="Binary"
allowAnonymous ="true" />
</ properties >
</ profile >
</ system.web >
</ configuration >
列表5 中有一個簡單購物籃的實現代碼,該購物籃擁有添加和刪除項(item)的方法(method),同時它還擁有兩個屬性(property),一個是用於獲 得該購物籃中的所有項的,一個是用於表示所有商品的總價的。
Imports Microsoft.VisualBasic
< Serializable() > _
Public Class ShoppingCart
Public _CartItems As New Hashtable()
' Return all the items from the Shopping Cart
Public ReadOnly Property CartItems() As ICollection
Get
Return _CartItems.Values
End Get
End Property
' The sum total of the prices
Public ReadOnly Property Total() As Decimal
Get
Dim sum As Decimal
For Each item As CartItem In _CartItems.Values
sum += item.Price * item.Quantity
Next
Return sum
End Get
End Property
' Add a new item to the shopping cart
Public Sub AddItem( ByVal ID As Integer , _
ByVal Name As String , ByVal Price As Decimal )
Dim item As CartItem = CType (_CartItems(ID), CartItem)
If item Is Nothing Then
_CartItems.Add(ID, New CartItem(ID, Name, Price))
Else
item.Quantity += 1
_CartItems(ID) = item
End If
End Sub
' Remove an item from the shopping cart
Public Sub RemoveItem( ByVal ID As Integer )
Dim item As CartItem = CType (_CartItems(ID), CartItem)
If item Is Nothing Then
Return
End If
item.Quantity -= 1
If item.Quantity = 0 Then
_CartItems.Remove(ID)
Else
_CartItems(ID) = item
End If
End Sub
End Class
< Serializable() > _
Public Class CartItem
Private _ID As Integer
Private _Name As String
Private _Price As Decimal
Private _Quantity As Integer = 1
Public ReadOnly Property ID() As Integer
Get
Return _ID
End Get
End Property
Public ReadOnly Property Name() As String
Get
Return _Name
End Get
End Property
Public ReadOnly Property Price() As Decimal
Get
Return _Price
End Get
End Property
Public Property Quantity() As Integer
Get
Return _Quantity
End Get
Set ( ByVal value As Integer )
_Quantity = value
End Set
End Property
Public Sub New ( ByVal ID As Integer , _
ByVal Name As String , ByVal Price As Decimal )
_ID = ID
_Name = Name
_Price = Price
End Sub
End Class
using System;
using System.Collections;
[Serializable]
public class ShoppingCart
{
public Hashtable _CartItems = new Hashtable();
// Return all the items from the Shopping Cart
public ICollection CartItems
{
get { return _CartItems.Values; }
}
// The sum total of the prices
public decimal Total
{
get
{
decimal sum = 0 ;
foreach (CartItem item in _CartItems.Values)
sum += item.Price * item.Quantity;
return sum;
}
}
// Add a new item to the shopping cart
public void AddItem( int ID, string Name, decimal Price)
{
CartItem item = (CartItem)_CartItems[ID];
if (item == null )
_CartItems.Add(ID, new CartItem(ID, Name, Price));
else
{
item.Quantity ++ ;
_CartItems[ID] = item;
}
}
// Remove an item from the shopping cart
public void RemoveItem( int ID)
{
CartItem item = (CartItem)_CartItems[ID];
if (item == null )
return ;
item.Quantity -- ;
if (item.Quantity == 0 )
_CartItems.Remove(ID);
else
_CartItems[ID] = item;
}
}
[Serializable]
public class CartItem
{
private int _ID;
private string _Name;
private decimal _Price;
private int _Quantity = 1 ;
public int ID
{
get { return _ID; }
}
public string Name
{
get { return _Name; }
}
public decimal Price
{
get { return _Price; }
}
public int Quantity
{
get { return _Quantity; }
set { _Quantity = value; }
}
public CartItem( int ID, string Name, decimal Price)
{
_ID = ID;
_Name = Name;
_Price = Price;
}
}
如 果你把列表5中的代碼添加到應用程序的App_Code目錄中,購物籃會自動被編譯。
在列表5中有一點值得 注意,那就是ShoppingCart和CartItem類都加上了可序列化的特性,這一點對於他們能否被序列化十分重要,只有這樣才能保存在Profile 對象中。
最後,列表6的頁面顯示了可以被添加到購物籃中的產品。購物籃是通過BindShoppingCart方法從Profile 對象中載入,該方法把購物籃中的對象綁定到一個GridView對象上,這些對象可以通過ShoppingCart類的CartItems屬性獲得。圖 2 在profile中存儲購物籃
AddCartItem方法用於在購物籃中添加一個產品,該方法中包含了檢測Profile 是否存在ShoppingCart的代碼。對於Profile中存儲的對象,你必須自己實例化這些對象,他們不會自動實例化。
RemoveCartItem方法用於從購物籃中移除一個產品,該方法只是簡單地通過調用Profile中的ShoppingCart 對象的RemoveItem方法。
< %@ Page Language = " VB " % >
< script runat = " server " >
Sub Page_Load()
If Not IsPostBack Then
BindShoppingCart()
End If
End Sub
Sub BindShoppingCart()
If Not Profile.ShoppingCart Is Nothing Then
CartGrid.DataSource = Profile.ShoppingCart.CartItems
CartGrid.DataBind()
lblTotal.Text = Profile.ShoppingCart.Total.ToString( " c " )
End If
End Sub
Sub AddCartItem( ByVal s As Object , ByVal e As EventArgs)
Dim row As GridViewRow = ProductGrid.SelectedRow
Dim ID As Integer = CInt (ProductGrid.SelectedDataKey.Value)
Dim Name As String = row.Cells( 1 ).Text
Dim Price As Decimal = CDec (row.Cells( 2 ).Text)
If Profile.ShoppingCart Is Nothing Then
Profile.ShoppingCart = New ShoppingCart
End If
Profile.ShoppingCart.AddItem(ID, Name, Price)
BindShoppingCart()
End Sub
Sub RemoveCartItem( ByVal s As Object , ByVal e As EventArgs)
Dim ID As Integer = CInt (CartGrid.SelectedDataKey.Value)
Profile.ShoppingCart.RemoveItem(ID)
BindShoppingCart()
End Sub
</ script >
< html >
< head >
< title > Products </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< table width = " 100% " >
< tr >
< td valign = " top " >
< h2 > Products </ h2 >
< asp:GridView
ID = " ProductGrid "
DataSourceID = " ProductSource "
DataKeyNames = " ProductID "
AutoGenerateColumns = " false "
OnSelectedIndexChanged = " AddCartItem "
ShowHeader = " false "
CellPadding = " 5 "
Runat = " Server " >
< Columns >
< asp:ButtonField
CommandName = " select "
Text = " Buy " />
< asp:BoundField
DataField = " ProductName " />
< asp:BoundField
DataField = " UnitPrice "
DataFormatString = " {0:c} " />
</ Columns >
</ asp:GridView >
< asp:SqlDataSource
ID = " ProductSource "
ConnectionString =
" Server=localhost;Database=Northwind;Trusted_Connection=true; "
SelectCommand =
" SELECT ProductID,ProductName,UnitPrice FROM Products "
Runat = " Server " />
</ td >
< td valign = " top " >
< h2 > Shopping Cart </ h2 >
< asp:GridView
ID = " CartGrid "
AutoGenerateColumns = " false "
DataKeyNames = " ID "
OnSelectedIndexChanged = " RemoveCartItem "
CellPadding = " 5 "
Width = " 300 "
Runat = " Server " >
< Columns >
< asp:ButtonField
CommandName = " select "
Text = " Remove " />
< asp:BoundField
DataField = " Name "
HeaderText = " Name " />
< asp:BoundField
DataField = " Price "
HeaderText = " Price "
DataFormatString = " {0:c} " />
< asp:BoundField
DataField = " Quantity "
HeaderText = " Quantity " />
</ Columns >
</ asp:GridView >
< b > Total: </ b >
< asp:Label ID = " lblTotal " Runat = " Server " />
</ td >
</ tr >
</ table >
</ form >
</ body >
</ html >
<% @ Page Language = " C# " %>
<% @ Import Namespace = " System.Globalization " %>
< script runat = " server " >
void Page_Load() {
if ( ! IsPostBack)
BindShoppingCart();
}
void BindShoppingCart()
{
if (Profile.ShoppingCart != null )
{
CartGrid.DataSource = Profile.ShoppingCart.CartItems;
CartGrid.DataBind();
lblTotal.Text = Profile.ShoppingCart.Total.ToString( " c " );
}
}
void AddCartItem(Object s, EventArgs e)
{
GridViewRow row = ProductGrid.SelectedRow;
int ID = ( int )ProductGrid.SelectedDataKey.Value;
String Name = row.Cells[ 1 ].Text;
decimal Price = Decimal.Parse(row.Cells[ 2 ].Text,
NumberStyles.Currency);
if (Profile.ShoppingCart == null )
Profile.ShoppingCart = new ShoppingCart();
Profile.ShoppingCart.AddItem(ID, Name, Price);
BindShoppingCart();
}
void RemoveCartItem(Object s, EventArgs e)
{
int ID = ( int )CartGrid.SelectedDataKey.Value;
Profile.ShoppingCart.RemoveItem(ID);
BindShoppingCart();
}
</ script >
< html >
< head >
< title > Products </ title >
</ head >
< body >
< form id = " form1 " runat = " server " >
< table width = " 100% " >
< tr >
< td valign = " top " >
< h2 > Products </ h2 >
< asp:GridView
ID = " ProductGrid "
DataSourceID = " ProductSource "
DataKeyNames = " ProductID "
AutoGenerateColumns = " false "
OnSelectedIndexChanged = " AddCartItem "
ShowHeader = " false "
CellPadding = " 5 "
Runat = " Server " >
< Columns >
< asp:ButtonField
CommandName = " select "
Text = " Buy " />
< asp:BoundField
DataField = " ProductName " />
< asp:BoundField
DataField = " UnitPrice "
DataFormatString = " {0:c} " />
</ Columns >
</ asp:GridView >
< asp:SqlDataSource
ID = " ProductSource "
ConnectionString =
" Server=localhost;Database=Northwind;Trusted_Connection=true; "
SelectCommand =
" SELECT ProductID,ProductName,UnitPrice FROM Products "
Runat = " Server " />
</ td >
< td valign = " top " >
< h2 > Shopping Cart </ h2 >
< asp:GridView
ID = " CartGrid "
AutoGenerateColumns = " false "
DataKeyNames = " ID "
OnSelectedIndexChanged = " RemoveCartItem "
CellPadding = " 5 "
Width = " 300 "
Runat = " Server " >
< Columns >
< asp:ButtonField
CommandName = " select "
Text = " Remove " />
< asp:BoundField
DataField = " Name "
HeaderText = " Name " />
< asp:BoundField
DataField = " Price "
HeaderText = " Price "
DataFormatString = " {0:c} " />
< asp:BoundField
DataField = " Quantity "
HeaderText = " Quantity " />
</ Columns >
</ asp:GridView >
< b > Total: </ b >
< asp:Label ID = " lblTotal " Runat = " Server " />
</ td >
</ tr >
</ table >
</ form >
</ body >
</ html >
繼承一個profile
你也 可以通過從一個已經存在的profile類中繼承一個profile來完成對profile的定義,這種特性能夠幫助你在 多個應用程序中使用相同的profile。
例如,列表7中列出了一個擁有多個用戶屬性的類,該類是從ProfileBase類繼承而來 的(你可以在System.Web.Profile中找到)