在上一篇文章中,我們讨論了使用 ASP.NET AJAX預設的Profile Service。一般來說,它已經能夠迎合大多數應用的需要了。不過除此之外,ASP.NET AJAX還提供了讓我們自定義Profile Service的機制。要自定義Profile Service,一般來說要分為兩步:
一、在ScriptManager中指定Profile Service的Path
在ASP.NET AJAX的用戶端腳本中,如果沒有使用Sys.Services.ProfileService.set_path方法來指定一個提供Profile Service的位址,就會使用預設的位址,它會使ASP.NET AJAX的Profile Service使用程式集中特定的類。一般來說,我們不需要手動調用Sys.Services.ProfileService.set_path方法,隻 需要在ScriptManager中指定即可。如下:
<asp:ScriptManager ID="ScriptManager1" runat="server" ScriptMode="Debug">
<ProfileService Path="CustomProfileService.asmx" />
</asp:ScriptManager>
打開頁面後,可以在頁面中發現如下的JavaScript代碼:

Sys.Services.ProfileService.set_path('/CustomProfileService.asmx');
出現“/”是因為我測試的頁面在根目錄下。是以,Profile Service就會使用指定的Web Service,而不是預設的Web Service類。
二、實作自己的Web Service類
指定了自己的Web Service類,自然就要實作自己的類了。事實上,我們要實作的就是3個方法。就這個方面來說,ASP.NET AJAX中Profile Service使用的預設的Web Service類Microsoft.Web.Profile.ProfileService是我們絕佳的參考。是以,我們在這裡分析一下這些方法,對于 我們的自定義工作是非常有幫助的。
可能需要注意的一點是,我們在實作這些方法時,從理論上來講參數類型不用完全和 Microsoft.Web.Profile.ProfileService中的方法完全相同。ASP.NET AJAX的能夠根據參數的類型盡可能地将獲得的JSON字元串轉換成需要的類型。不過事實上,似乎 Microsoft.Web.Profile.ProfileService裡那些方法的參數選擇已經是非常合理的。另外,由于用戶端Profile Service代碼不太容易修改(事實上用戶端也不是不能擴充,最極端的情況,不就是我們自己實作一個ProfileService嗎?),為了保持傳回 的JSON字元串能夠被正确處理,這些方法的傳回值一般來說可以不變。
1、GetAllPropertiesForCurrentUser方法
這個方法的作用是獲得目前使用者所有的Profile資訊,它沒有輸入的參數,傳回的JSON字元串形式如下:
{
'ZipCode' : ...,
'Address.City' : ...,
'Address.State' : ...
}
它通過GroupName.ProfileName的形式來表示Profile Group,Group中的每一個Profile需要分别列出,而“...”則表示對應Profile值的JSON字元串。
在Microsoft.Web.Profile.ProfileService裡,這個方法的代碼如下:
[WebMethod]
public IDictionary<string, object> GetAllPropertiesForCurrentUser()
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, null);
2、GetPropertiesForCurrentUser方法
這個方法的作用是獲得目前使用者指定的Profile資訊,它的輸入JSON字元串形式如下:
['ZipCode', 'Address.City', 'Address.State']
它的傳回值的JSON字元串和GetAllPropertiesForCurrentUser相同,就不再贅述了。
在類中,這個方法的代碼如下:
public IDictionary<string, object> GetPropertiesForCurrentUser(string[] properties)
ProfileService.CheckProfileServicesEnabled();
return ProfileService.GetProfile(HttpContext.Current, properties);
可以看到,GetAllPropertiesForCurrentUser和GetPropertiesForCurrentUser中都是使用了ProfileService.GetProfile靜态方法來獲得結果的,我們來仔細看一下這個方法的實作。如下:
GetProfile靜态方法
這個方法還是非常容易了解和編寫的,不需要涉及到任何序列化或者反序列化操作,那些工作完全由ASP.NET的Web Service Access機制代為完成了。
3、SetPropertiesForCurrentUser方法
這個方法的作用是儲存目前使用者的Profile資訊,它的輸入JSON字元串形式如下:
而它傳回的則是正确儲存的Profile數量。代碼如下:
public int SetPropertiesForCurrentUser(IDictionary<string, object> values)
return ProfileService.SetProfile(HttpContext.Current, values);
起關鍵作用的方法是ProfileService.SetProfile靜态方法,我們來仔細看一下這個方法:
SetProfile靜态方法
方法也不難了解,不過可以需要對于ASP.NET AJAX的序列化與反序列化能力有一定了解,例如第22和23行構造了一個JavaScriptSerializer的目的是使用包含在 WebServiceData内的JavaScriptTypeResolver資訊,以此獲得從字元串形式的type描述到确定type類型的映射關 系。這其實是一個非常有用的特性,不過有點讓人想不通的是,在目前的WebServiceData中由于沒有方法添加自定義的 JavaScriptTypeResolver,是以這個功能的效用為0。難道未來的版本會有辦法利用到這個特性?拭目以待吧,雖然我覺得比較困難。
我們也可以看到,這個方法中使用了内部的ObjectConverter.ConvertObjectToType方法來将一個嵌套的 Dictionay和List轉換為指定的類型。如果我們要使用這個方法,應該怎麼做呢?事實上,ASP.NET AJAX提供了我們一定的序列化與反序列化能力。請看JavaScirptSerializer的公有執行個體方法ConvertToType< T>的實作:
public T ConvertToType<T>(object obj)
return (T) ObjectConverter.ConvertObjectToType(obj, typeof(T), this);
它直接調用了内部的ObjectConverter.ConverObjectToType方法,這不就是我們所需要的功能嗎?
等一下!别高興的太早!請注意,這是一個範型方法!我們這裡隻能獲得一個Profile屬性的類型對象,不能在編譯期指定調用哪種具體類型的範型方法, 也就是說,我們不能在這裡簡單地使用這個範型方法。我們在這裡需要對範型方法的調用進行“後期綁定”,因為隻有在執行期才能獲得範型的類型。
後期綁定?這不就是Reflection提供的功能嗎?是以,我們可以使用.NET Framework 2.0的Reflection機制,它已經對于範型類型提供了支援。在這裡,我們可以這麼做:
JavaScriptSerializer serializer = new JavaScriptSerializer();
Type type = value.GetType(); // 獲得所需的Profile屬性的Type對象
MethodInfo info = typeof(JavaScriptSerializer).GetMethod("ConvertToType").MakeGenericMethod(type);
return info.Invoke(serializer, new object[] { value });
這樣,我們就實作了對于範型方法調用的“後期綁定”:在執行期才決定調用哪個具體類型的範型方法。這麼做會有性能損失,例如查找 ConvertToType方法并構造相應的範型的MethodInfo,但是如果我們使用Dictionary<Type, MethodInfo>将type和它所對應的MethodInfo儲存起來,可以在一定程度上的減少性能的損失。不過使用 Method.Invoke造成的性能損失就無法避免了。
另外,我打算在接下來的文章中詳細分析一下ASP.NET AJAX中提供給開發人員的序列化與反序列化能力,以及它們是如何配合JavaScriptTypeResolver與JavaScriptConverter提供一定的自定義能力。
我們現在已經知道了如何自定義伺服器端的Profile Service支援,但是如果我們一直使用用戶端的“标準”功能,還談不上“自定義”或者“擴充”。那麼在下一片文章中,我們一起來讨論一下自定義用戶端的Profile Service支援吧。
本文轉自 jeffz 51CTO部落格,原文連結:http://blog.51cto.com/jeffz/60667,如需轉載請自行聯系原作者