
4.5 ProfileService
ASP.NET AJAX提供了另一个内建的Service——Profile Service,与Authentication Service相同,Profile Service也是与ASP.NET所提供的Profile结合,允许设计师通过JavaScript来访问位于Server端的Profile信息。与Authentication Servce结合后,Profile Service可以让设计师在不刷新网页的状态下,实现无刷新登录及无刷新带出用户设定的功能。最常见的应用例子就是于门户网站常见的,在使用者登录后,在首页的某个区域显示该用户所设定的友情链接功能。那具体该如何做呢?请照着下面步骤,在前例的Authentication网页中添加Profile的功能。
1. 在SecureFolder中新增一个名为ProfileTest.aspx的网页。
2. 在页面中加入一个ScriptManager控件。
3. 放入显示和输入Profile信息的控件及访问Profile Service的JavaScript程序代码,如程序4-17所示。
4. 在web.config启动Profile功能,这里有两个区段,一个启动ASP.NET内建的Profile功能,另一个启用ASP.NET AJAX所提供的Profile Service,如程序4-18所示。
5. 在Default.aspx网页中添加一个HyperLink控件,NavigateUrl设为/SecureFolder/Profile-Test.aspx,Text设为Go Profile Form。
程序4-17
Samples\4\AuthenticationTest\SecureFolder\ProfileTest.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProfileTest.aspx.cs" Inherits="ProfileTest" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <script language=javascript> //Profile成功读取时调用的函数 function OnLoadCompleted(numProperties, userContext, methodName) { $get("txtData").value = Sys.Services.ProfileService.properties.Data; $get("Text2").value = Sys.Services.ProfileService.properties. ComplexData.Data1; $get("Text3").value = Sys.Services.ProfileService.properties. ComplexData.Data2; } //Profile成功储存时调用的函数 function OnSaveCompleted(numProperties, userContext, methodName) { alert("OK"); } //读取或储存Profile时,失败时调用的函数 function OnProfileFailed(error_object, userContext, methodName) { alert("Profile service failed with message: " + error_object.get_message()); } //储存Profile信息 function SaveProfile() { Sys.Services.ProfileService.properties.Data = $get("txtData").value; Sys.Services.ProfileService.properties.ComplexData.Data1 = $get('Text2').value; Sys.Services.ProfileService.properties.ComplexData.Data2 = $get('Text3').value; Sys.Services.ProfileService.save(null,OnSaveCompleted, OnProfileFailed, null); } //读取Profile信息 function LoadProfile() { Sys.Services.ProfileService.load(null,OnLoadCompleted, OnProfileFailed, null); } //在网页载入时调用的函数,此处调用LoadProfile来读取使用者的Profile function pageLoad() { LoadProfile(); } </script> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> Data: <input id="txtData" type=text /><br /> ComplexData.Data1: <input id="Text2" type=text /><br /> ComplexData.Data2: <input id="Text3" type=text /> <input id="Button1" type="button" value="Save Profile" honclick= "SaveProfile()" /></div> </form> </body> </html>
程序4-18
Samples\4\AuthenticationTest\web.config <system.web> ............. <!-- 启动ASP.NET的Profile功能--> <profile enabled="true"> <!-- 定义Profile中的属性 --> <properties> <!-- 单一属性 --> <add name="Data" type="System.String" defaultValue=""/> <!-- 复杂属性 --> <group name="ComplexData"> <add name="Data1" type="System.String"/> <add name="Data2" type="System.String"/> </group> </properties> </profile> <system.web.extensions> <scripting> <webServices> <authenticationService enabled="true"/> <!-- 启用ASP.NET AJAX的Profile Service, readAccessProperties中可设定允许Profile Service读取的Profile属性, writeAccessProperties则设定允许写入的属性,每个属性都必须以;分隔, 当有复杂属性时,必须以<Group Name>.<Property Name>方式于此指定--> <profileService enabled="true" readAccessProperties="Data,ComplexData.Data1, ComplexData.Data2" writeAccessProperties="Data,ComplexData.Data1, ComplexData.Data2"/> </webServices> </scripting> </system.web.extensions> ...........
在使用者登录后,便可点击链接浏览ProfileTest.aspx网页,此时pageLoad函数会被调用,本例子调用了LoadProfile函数读取该使用者的Profile,LoadProfile函数则是调用了ASP.NET AJAX所提供的ProfileService对象的load函数来读取Profile信息。与Authentication Service相同,在调用load函数时,必须分别指定当读取成功、读取失败时所调用的函数,此处是设定为成功时调用OnLoadCompleted函数,失败时则调用OnProfileFailed函数,load函数的第一个参数一个数组类型,可让设计师明确指定要读取的Profile属性,如该属性为null,代表着要将web.config中所指定可读取的Profile属性读回。此参数的设计用途很明显,并不是每个网页都需要全部的Profile信息,明确指定需要读回的Profile属性,可提高网页的性能,如程序4-19 所示,便是仅读回ComplexData.Data1 及ComplexData.Data2 的Profile属性值,而不读取Data属性。
程序4-19
Sys.Services.ProfileService.load(["ComplexData.Data1","ComplexData.Data2"], OnLoadCompleted, OnProfileFailed, null);
当Profile属性读回后,设计师便可通过Sys.Services.ProfileService.properties.<属性名>来访问该Profile属性值。当需要储存Profile属性时,则必须调用Sys.Services.ProfileService.save函数,与load函数相同,第一个参数是数组类型,可明确指定要储存的Profile属性名称,第二及第三个参数则是指定储存成功及失败所调用的函数。读者们可执行此程序,在登录后点击Go Profile Form,输入一些信息,点击储存后,将浏览器关闭,再次执行并登录后点击Go Profile Form,即可看到先前所输入的信息,如图4-11所示。

图4-11
不是说要做出友情链接的功能吗?这个范例未免也太简单了吧?呵,要实现这个功能也不难,请照着以下步骤做。
1. 在web.config中新增一个Profile properties来储存联结,这是一个StringCollection类型的属性,别忘了设定Profile Service的readAccessProperties及writeAccessProperties哦,如程序4-20所示。
2. 修改ProfileTest.aspx,加入可以让使用者键入联结的控件,如程序4-21所示。
程序4-20
Samples\4\AuthenticationTest\web.config <!-- 启动ASP.NET的Profile功能--> <profile enabled="true"> <!-- 定义Profile中的属性 --> <properties> <!-- 单一属性 --> <add name="Data" type="System.String" defaultValue=""/> <!-- 复杂属性 --> <group name="ComplexData"> <add name="Data1" type="System.String"/> <add name="Data2" type="System.String"/> </group> <add name="Links" type="System.Collections.Specialized. StringCollection" /> </properties> </profile> ............... <system.web.extensions> <scripting> <webServices> <authenticationService enabled="true"/> <!-- 启用ASP.NET AJAX的Profile Service, readAccessProperties中可设定允许Profile Service读取的Profile属性, writeAccessProperties则设定允许写入的属性,每个属性都必须以;分隔, 当有复杂属性时,必须以<Group Name>.<Property Name>方式于此指定--> <profileService enabled="true" readAccessProperties="Data,ComplexData.Data1,ComplexData. Data2,Links" writeAccessProperties="Data,ComplexData.Data1,ComplexData. Data2,Links"/> </webServices> </scripting> </system.web.extensions>
程序4-21
Samples\4\AuthenticationTest\SecureFolder\ProfileTest.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProfileTest.aspx.cs"
Inherits="ProfileTest" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<script language=javascript>
function AddLink()
{
var links = $get("links");
links.options.length = links.length+1;
links.options[links.length-1]=
new Option($get("txtNewLink").value,$get("txtNewUrl").value);
}
function OnLoadCompleted(numProperties, userContext, methodName)
{
$get("txtData").value = Sys.Services.ProfileService.properties.Data;
$get("Text2").value = Sys.Services.ProfileService.properties.
ComplexData.Data1;
$get("Text3").value = Sys.Services.ProfileService.properties.
ComplexData.Data2;
$get("links").options.length = 0;
$get("links").options.length =
Sys.Services.ProfileService.properties.Links.length;
for(var i = 0; i < Sys.Services.ProfileService.properties.Links.
length; i++)
{
var s = Sys.Services.ProfileService.properties.Links[i].split(',');
if(s.length == 2)
$get("links").options[i] = new Option(s[0],s[1]);
else
$get("links").options[i] = new Option(s[0],s[0]);
}
}
function OnSaveCompleted(numProperties, userContext, methodName)
{
alert("OK");
}
function OnProfileFailed(error_object, userContext, methodName)
{
alert("Profile service failed with message: " +
error_object.get_message());
}
function SaveProfile()
{
Sys.Services.ProfileService.properties.Data = $get("txtData").value;
Sys.Services.ProfileService.properties.ComplexData.Data1 =
$get('Text2').value;
Sys.Services.ProfileService.properties.ComplexData.Data2 =
$get('Text3').value;
var links = $get("links");
Sys.Services.ProfileService.properties.Links = new
Array(links.options.length);
for(var i = 0; i < links.options.length; i++)
Sys.Services.ProfileService.properties.Links[i] =
links.options[i].text +","+ links.options[i].value;
Sys.Services.ProfileService.save(null,OnSaveCompleted,
OnProfileFailed, null);
}
function LoadProfile()
{
Sys.Services.ProfileService.load(null,OnLoadCompleted,
OnProfileFailed, null);
}
function pageLoad()
{
LoadProfile();
}
</script>
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
Data:
<input id="txtData" type=text /><br />
ComplexData.Data1:
<input id="Text2" type=text /><br />
ComplexData.Data2:
<input id="Text3" type=text />
<br />
<select id=" links" name="links">
<option selected="selected"></option>
</select>
Name
<input id="txtNewLink" type="text" />
Url<input id="txtNewUrl" type="text" />
<input id="btnAdd" type="button" value="Add" onclick=
"AddLink()" /><br />
<input id="Button1" type="button" value="Save Profile" onclick=
"SaveProfile()" />
</div>
</form>
</body>
</html>
执行此程序后,就可以在ProfileTest.aspx中新增链接,这些链接信息会以<名称>,<Url>的方式存在Profile的Links属性中,而Links属性是一个字符串数组,这代表着用户可储存1 个以上的链接信息至Links属性中,如图4-12所示。

图4-12
如果不用Select来显示链接,而真正地使用超链接呢?很简单,只要放个div控件到网页中,在更新Select时也顺便更新它就行了,如程序4-22所示。
程序4-22
Samples\4\AuthenticationTest\SecureFolder\ProfileTest.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="ProfileTest.aspx.cs" Inherits="ProfileTest" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Untitled Page</title> </head> <body> <form id="form1" runat="server"> <div> <script language=javascript> function LoadLinksArea() { var linkArea = $get("LinksArea"); var s = "<table>"; for(var i = 0; i < Sys.Services.ProfileService.properties.Links. length; i++) { var linkUrl = Sys.Services.ProfileService.properties .Links[i].split(","); s = s + "<tr><td><a href='http://"+linkUrl[1]+"'>"+linkUrl[0] +"</td></tr>"; } linkArea.innerHTML = s+"</table>"; } function RefreshLinksArea() { var linkArea = $get("LinksArea"); var s = "<table>"; var links = $get("Links"); for(var i = 0; i < links.options.length; i++) { s = s + "<tr><td><a href='http://"+links.options[i].value+"'>"+ links.options[i].text+"</td></tr>"; } linkArea.innerHTML = s+"</table>"; } function AddLink() { var links = $get("Links"); links.options.length = links.length+1; links.options[links.length-1]= new Option($get("txtNewLink").value, $get("txtNewUrl").value); RefreshLinksArea(); } function OnLoadCompleted(numProperties, userContext, methodName) { $get("txtData").value = Sys.Services.ProfileService.properties.Data; $get("Text2").value = Sys.Services.ProfileService.properties.ComplexData.Data1; $get("Text3").value = Sys.Services.ProfileService.properties.ComplexData.Data2; $get("Links").options.length = 0; $get("Links").options.length = Sys.Services.ProfileService.properties.Links.length; for(var i = 0; i < Sys.Services.ProfileService.properties.Links.length; i++) { var s = Sys.Services.ProfileService.properties.Links[i].split(','); if(s.length == 2) $get("Links").options[i] = new Option(s[0],s[1]); else $get("Links").options[i] = new Option(s[0],s[0]); } LoadLinksArea(); } function OnSaveCompleted(numProperties, userContext, methodName) { alert("OK"); } function OnProfileFailed(error_object, userContext, methodName) { alert("Profile service failed with message: " + error_object.get_message()); } function SaveProfile() { Sys.Services.ProfileService.properties.Data = $get("txtData").value; Sys.Services.ProfileService.properties.ComplexData.Data1 = $get('Text2').value; Sys.Services.ProfileService.properties.ComplexData.Data2 = $get('Text3').value; var links = $get("Links"); Sys.Services.ProfileService.properties.Links = new Array(links.options.length); for(var i = 0; i < links.options.length; i++) Sys.Services.ProfileService.properties.Links[i] = links.options[i].text +","+ links.options[i].value; Sys.Services.ProfileService.save(null,OnSaveCompleted, OnProfileFailed, null); } function LoadProfile() { Sys.Services.ProfileService.load(null,OnLoadCompleted, OnProfileFailed, null); } function pageLoad() { LoadProfile(); } </script> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> Data: <input id="txtData" type=text /><br /> ComplexData.Data1: <input id="Text2" type=text /><br /> ComplexData.Data2: <input id="Text3" type=text /> <br /> <select id="Select1" name="links"> <option selected="selected"></option> </select> Name <input id="txtNewLink" type="text" /> Url<input id="txtNewUrl" type="text" /> <input id="btnAdd" type="button" value="Add" onclick= "AddLink()" /><br /> <div id=LinksArea> </div> <input id="Button1" type="button" value="Save Profile" onclick="SaveProfile()" /> </div> </form> </body> </html>
完成后的结果如图4-13所示。

图4-13
你是否有种感觉,innerHTML加上div后,相当地好用呢?
ProfileGroup
在多数情况下,我们都会在网页载入时调用ProfileService.load函数来读取该使用者的Profile,因此相关的Profile属性在读取成功后,便会定义于ProfileService.properties中,设计师可以直接访问这些属性。但是若在未调用load函数情况下访问ProfileService.properties时,这些属性事实上是不存在的,不过由于JavaScript的语法,当访问的变量不存在时,即自动声明该变量,并给予undefine的值,所以这样的写法不会引发语法错误。只是当该属性是复杂属性,例如ComplexData时,直接键入ProfileService.properties.ComplexData.Data1,这种语法就不能执行了,因为JavaScript虽然会为我们声明ComplexData变量,但由于其值是默认的undefine,访问其Data1变量便成了不合法的动作,如程序4-23所示。
程序4-23
function ManualDefine() { Sys.Services.ProfileService.properties.Data = "TEST"; //不合法的访问,因为ComplexData是undefine. Sys.Services.ProfileService.properties.ComplexData.Data1 = "TEST1"; //不合法的访问,因为ComplexData是undefine. Sys.Services.ProfileService.properties.ComplexData.Data2 = "TEST2"; } function pageLoad() { //LoadProfile(); ManualDefine(); }
此时必须依赖Sys.Services中所定义的ProfileGroup对象,以程序4-24的方式来赋值。
程序4-24
function ManualDefine() { Sys.Services.ProfileService.properties.Data = "TEST"; Sys.Services.ProfileService.properties.ComplexData = new Sys.Services.ProfileGroup(); Sys.Services.ProfileService.properties.ComplexData.Data1 = "TEST1"; Sys.Services.ProfileService.properties.ComplexData.Data2 = "TEST2"; Sys.Services.ProfileService.properties.Links = new Array(2); Sys.Services.ProfileService.properties.Links[0] = "Hinet,www.hinet.net"; Sys.Services.ProfileService.properties.Links[1] = "ASP.NET,www.asp.net"; Sys.Services.ProfileService.save(null,OnSaveCompleted, OnProfileFailed,null); }
程序4-24是模拟未调用ProfileService的load函数,以程序的方式来储存Profile时的做法。