1.3 使用用户配置文件
ASP.NET 2.0 Framework提供了一种可选的不同于cookie和Session状态的方式存储用户信息:Profile对象。Profile对象提供强类型、可持久化的Session状态表单。
可以在应用程序的根Web配置文件定义一组Profile属性来创建Profile。ASP.NET Framework在后台动态编译一个包含这些属性的类。
例如,代码清单1-19所示的Web配置文件定义了一个Profile包含了三个属性:firstName、lastName和numverOfVisits。
代码清单1-19 Web.config
<system.web>
<profile>
<properties>
<add name="firstName" />
<add name="lastName" />
<add name="numberOfVisits" type="Int32" defaultValue="0" />
</properties>
</profile>
</system.web> </configuration>
当定义Profile属性时,可以使用下面的属性:
q name——用于指定属性名称;
q type——用户指定属性类型。类型可以是任意类型,包括定义在App_Code文件夹中的自定义组件(默认值是字符串类型);
q defaultValue——用于指定属性默认值;
q readOnly——用于创建只读属性(默认值为false);
q serializeAs——用于指定一个属性如何持久化为静态持久化数据。可能的值有Binary、ProviderSpecific、String和Xml(默认值为ProviderSpecific);
q allowAnonymous——用于允许匿名用户读写属性(默认值为false);
q provider——用于关联属性到特定的Profile提供程序;
q customProviderData——用于传递自定义数据到Profile提供程序。
在Web配置文件中定义Profile后,可以使用Profile对象修改Profile属性。例如,代码清单1-20所示的页面使用一个表单修改firstName和lastName属性的值。并且,页面在每次被请求时自动更新numberOfVisits属性(见图1-7)。
代码清单1-20 ShowProfile.aspx
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
> <script runat="server">void Page_PreRender()
{
lblFirstname.Text = Profile.firstName;
lblLastName.Text = Profile.lastName;
Profile.numberOfVisits++;
lblNumberOfVisits.Text = Profile.numberOfVisits.ToString();
}
protected void btnUpdate_Click(object sender, EventArgs e)
{
Profile.firstName = txtNewFirstName.Text;
Profile.lastName = txtNewLastName.Text;
}
</script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"><title>Show Profile</title> </head> <body>
<form id="form1" runat="server">
<div>
First Name:
<asp:Label
id="lblFirstname"
Runat
="server" /><br /><br />
Last Name:
<asp:Label
id="lblLastName"
Runat
="server" /><br /><br />
Number of Visits:
<asp:Label
id="lblNumberOfVisits"
Runat
="server" />
<hr />
<asp:Label
id="lblNewFirstName"
Text
="New First Name:"AssociatedControlID
="txtNewFirstName"Runat
="server" /><asp:TextBox
id="txtNewFirstName"
Runat
="server" /><br /><br />
<asp:Label
id="lblNewLastName"
Text
="New Last Name:"AssociatedControlID
="txtNewLastName"Runat
="server" /><asp:TextBox
id="txtNewLastName"
Runat
="server" /><br /><br />
<asp:Button
id="btnUpdate"
Text
="Update Profile"OnClick
="btnUpdate_Click"Runat
="server" />
</div>
</form> </body> </html>
图1-7 显示Profile信息
注意,Profile属性被公开为强类型属性。例如,numberOfVisits属性被公开为一个整型属性,因为我们将其定义为一个整型属性。
理解Profile属性是持久化的非常重要。如果为一个用户设置Profile属性,那么就算该用户过500年也不会再返回网站,属性也会保留其值。不像Session状态,当为Profile属性赋值时,用户离开网站,值也不会消失。
Profile对象使用提供程序模型。默认的Profile提供程序是SqlProfileProvider。默认情况下,该提供程序保存Profile数据到名为ASPNETDB.mdf的Microsoft SQL Server 2005 Express数据库,保存在应用程序的App_Code文件夹。如果数据库不存在,第一次使用Profile对象时它会被自动创建。
默认情况下,不能为匿名用户保存Profile信息。ASP.NET Framework使用用户的身份认证标识关联Profile信息。对任何标准的ASP.NET Framework支持的身份验证方式,都可以使用Profile对象,包括表单身份认证和Windows身份认证(默认情况下,Windows身份验证被启用)。
注解 本节的后续部分,你将了解到如何为匿名用户保存Profile信息。
1.3.1 创建用户配置文件组
如果需要定义许多Profile属性,则需要将属性组织成组,使得属性更易管理。例如,代码清单1-21所示的Web配置文件定义了两个名叫Preference和ContactInfo的组。
代码清单1-21 Web.config
<system.web>
<profile>
<properties>
<group name="Preferences">
<add name="BackColor" defaultValue="lightblue"/>
<add name="Font" defaultValue="Arial"/>
</group>
<group name="ContactInfo">
<add name="Email" defaultValue="Your Email"/>
<add name="Phone" defaultValue="Your Phone"/>
</group>
</properties>
</profile>
</system.web> </configuration>
代码清单1-22所示的页面演示如何读写不同组的属性。
代码清单1-22 ShowProfileGroups.aspx
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
> <script runat="server">void Page_Load()
{
// Display Contact Info
lblEmail.Text = Profile.ContactInfo.Email;
lblPhone.Text = Profile.ContactInfo.Phone;
// Apply Preferences
Style pageStyle = new Style();
pageStyle.BackColor = ColorTranslator.FromHtml(Profile.Preferences.BackColor);
pageStyle.Font.Name = Profile.Preferences.Font;
Header.StyleSheet.CreateStyleRule(pageStyle, null, "html");
}
</script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"><title>Untitled Page</title> </head> <body>
<form id="form1" runat="server">
<div>
Email:
<asp:Label
id="lblEmail"
Runat
="server" /><br /><br />
Phone:
<asp:Label
id="lblPhone"
Runat
="server" />
</div>
</form> </body> </html>
1.3.2 支持匿名用户
默认情况下,匿名用户不能修改Profile属性。问题在于ASP.NET Framework没有办法关联Profile数据和特定的用户,除非用户是经过身份鉴别的。
如果希望允许匿名用户修改Profile属性,必须启用ASP.NET Framework的名叫匿名认证(Anonymous Identification)的功能。当匿名认证功能开启时,一个标识(一个GUID)被分配给匿名用户,并保存在持久化的浏览器cookie中。
注解 可以启用不依赖cookie的匿名标识符。不依赖cookie的匿名标识符与不依赖cookie的会话类似:匿名标识符被添加到页面的URL而不是cookie。可以在配置文件中设置anonymousIdenti- fication元素的cookieless属性值为UseURI或AutoDetect,来启用不依赖cookie的匿名标识符。
并且,必须用allowAnonymous标识所有希望匿名用户可以修改的Profile属性。例如,代码清单1-23所示的Web配置文件启用了匿名身份认证,并定义了一个可以被匿名用户修改的Profile属性。
代码清单1-23 Web.config
<system.web>
<authentication mode="Forms" />
<anonymousIdentification enabled="true" />
<profile>
<properties>
<add
name="numberOfVisits"
type
="Int32"defaultValue
="0"allowAnonymous
="true" /></properties>
</profile>
</system.web> </configuration>
代码清单1-23中定义的numberOfVisits属性包含了allowAnonymous属性。注意,Web配置文件同样启用了表单鉴别。当启用表单鉴别,并且没有登录时,用户就是匿名用户。
代码清单1-24所示的页面演示了当匿名身份认证启用时,如何修改Profile属性。
代码清单1-24 ShowAnonymousIdentification.aspx
"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();
}
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"Runat
="server" />
<asp:Button
id="btnLogout"
Text
="Logout"OnClick
="btnLogout_Click"Runat
="server" />
</div>
</form> </body> </html>
每次请求代码清单1-24所示的页面时,numberOfVisits这个Profile属性加1并显示。页面包含了3个按钮:Reload,Login和Logout(见图1-8)。
页面同样显示了Profile.UserName属性的值。该属性或者代表当前的用户名,或者代表匿名用户标识符。numberOfVisits这个Profile属性的值在Profile.UserName属性的值之后显示。
点击Reload按钮会很快地重新加载页面,并增加numberOfVisits属性的值。
点击Login按钮,则Profile.UserName变为Bob。numberOfVisits属性被重置。
点击Logout按钮,则Profile.UserName属性切换回显示匿名用户标识符。numberOfVisits属性变回它前一个值。
图1-8 创建匿名用户配置文件
1.3.3 合并匿名用户配置文件
在前一部分,我们已经看到,当用户从匿名切换到鉴别状态时,所有的用户配置信息会丢失。例如,如果在Profile对象中存储了购物车,登录后则所有的购物车项目会丢失。
可以在用户从匿名切换到鉴别状态时,处理Global.asax文件中的MigrateAnonymous事件,预存Profile属性的值。该事件在拥有用户配置的用户登录时触发。
例如,代码清单1-25中,MigrateAnonymous事件自动复制所有的匿名Profile属性到用户当前通过验证的Profile。
代码清单1-25 Global.asax
void Application_Start(object sender, EventArgs e)
{
Application["SessionCount"] = 0;
}
void Session_Start(object sender, EventArgs e)
{
Application.Lock();
int count = (int)Application["SessionCount"];
Application["SessionCount"] = count + 1;
Application.UnLock();
}
void Session_End(object sender, EventArgs e)
{
Application.Lock();
int count = (int)Application["SessionCount"];
Application["SessionCount"] = count - 1;
Application.UnLock();
}
//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();
//}
//public void Profile_ProfileAutoSaving(object s, ProfileAutoSaveEventArgs e)
//{
// if (Profile.ShoppingCart.HasChanged)
// e.ContinueWithProfileAutoSave = true;
// else
// e.ContinueWithProfileAutoSave = false;
//}
</script>
当用户的匿名标识传递给Profile.GetProfile()方法时,关联用户的匿名Profile会返回。接着,所有匿名Profile的属性被复制到当前Profile。最后,匿名Profile被删除,匿名标识被销毁(如果不销毁匿名标识,则MigrateAnonymous事件在登录认证后的每次页面请求都会被执行)。
1.3.4 从自定义类继承Profile
除了在Web配置文件中定义Profile属性列表,也可以在一个类中定义Profile属性。例如,代码清单1-26所示的类包含了名为FirstName和LastName的两个属性。
代码清单1-26 App_Code\SiteProfile.cs
{
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; }
}
}
注意,代码清单1-26所示的类从BaseProfile类继承。
声明类之后,就可以在Web配置文件中通过从该类继承Profile对象来定义Profile。代码清单1-27所示的Web配置文件使用inherits属性从SiteProfile类继承Profile。
代码清单1-27 Web.config
<system.web>
<anonymousIdentification enabled="true" />
<profile inherits="SiteProfile" />
</system.web> </configuration>
从Web配置文件中继承Profile之后,就可以以正常的方式使用这个Profile了。可以通过访问Profile的属性读写在SieProfile类中定义的任何属性。
注解 随书附带资源中包含一个名叫ShowSiteProfile.aspx的页面,用于显示代码清单1-27中定义的Profile属性。
注解 如果从一个类继承Profile属性,同时在Web配置文件中定义Profile属性,则两组Profile属性将会被合并。
当在一个类中定义Profile属性时,可以使用下面的attribute修饰那些属性:
q SettingAllowAnonymous——用于允许匿名用户读写属性;
q ProfileProvider——用于关联属性到一个特定的Profile提供程序;
q CustomProviderData——用于传递自定义数据到Profile提供程序。
例如,所有声明于代码清单1-28中的SiteProfile类的属性都包含了SettingAllowAnonymous attribute,允许匿名用户读写这些属性。
1.3.5 创建复杂Profile属性
到目前为止,我们已经用Profile属性表达过字符串和整数这些简单类型了。我们也可以使用Profile属性表达诸如自定义购物车这样的复杂类。
例如,代码清单1-28所示的类表示一个简单的购物车。
代码清单1-28 App_Code\ShoppingCart.cs
{
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; }
}
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;}
}
}
代码清单1-28中的文件实际上包含了两个类:ShoppingCart类和CartItem类。ShoppingCart类公开了一个CartItem对象的集合。
代码清单1-29所示的Web配置文件定义了一个名为ShoppingCart的Profile属性来表示ShoppingCart类。type属性设为ShoppingCart类的完全标识名称。
代码清单1-29 Web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<profile>
<properties>
<add name="ShoppingCart" type="AspNetUnleashed.ShoppingCart" />
</properties>
</profile>
</system.web>
</configuration>
最后,代码清单1-30所示的页面使用了Profile.ShoppingCart属性。ShoppingCart的