ASP.NET AJAX提供了Profile Service,允許開發人員異步地從伺服器端通路Profile資訊。從RTM開始,用戶端的Profile Service還提供了對于Profile Group的支援,是以可以說已經相當成熟了。那麼對于Profile Service的細節,是否大家都了解了呢?從ScriptManager的使用上來看,ProfileService是能夠擴充的,那麼應該如何擴充呢?細心的朋友們應該也發現了,在web.config中也增加了對于Profile Service的配置,那麼這些配置應該如何使用呢?
在這篇文章裡,我們将一起來讨論Profile Service使用方式的一些細節。
1、ASP.NET Profile基礎
這部分簡單地描述了ASP.NET Profile的使用,如果已經了解ASP.NET Profile的朋友就可以跳過這部分了。
在ASP.NET中,可以使用Profile,隻需在web.config中進行定義即可。例如:
<system.web>
<anonymousIdentification enabled="true" />
<profile enabled="true" defaultProvider="SqlProvider" >
<providers>
<add
name="SqlProvider"
connectionStringName="SqlServices"
applicationName="ProfileBaseApplication"
type="System.Web.Profile.SqlProfileProvider" />
</providers>
<properties>
<add name="ZipCode" allowAnonymous="true" />
<add name="RecentSearchList"
type="System.Collections.Specialized.StringCollection"
serializeAs="Xml"
allowAnonymous="true" />
</properties>
</profile>
</system.web>
在運作時,則可以通過目前Context的Profile屬性獲得目前使用者的Profile資訊,Profile屬性是一個繼承ProfileBase的ProfileCommon類執行個體,根據web.config的中的定義提供了強類型的屬性通路。例如:
public void Page_Load(object sender, EventArgs e)
{
this.Profile.ZipCode = "...";
}
在web.config中也可以定義ProfileGroup。例如:
<properties>
<add name="ZipCode" />
<group name="Address">
<add name="Street" />
<add name="City" />
<add name="State" />
<add name="CountryOrRegion" />
</group>
</properties>
這樣就定義了一個ProfileGroup,它會被定義成一個ProfileGroupBase的子類ProfileGroupXXXX(例如上面的定義則是ProfileGroupAddress),然後這個ProfileGroup也提供了相應的強類型屬性。這樣,就可以通過如下的方式通路到:
this.Profile.Address.City = "...";
Profile是一個非常容易使用的東西,我們能夠為其指派,它能夠使用指定Provider自動地進行儲存。另外在web.config檔案中,我們也能夠為Profile的的各項值定義各種屬性,例如是否能夠被匿名使用者使用,是否隻讀,甚至讓ProfileCommon類繼承自定義的Profile子類。詳細資訊,請感興趣的朋友們參考MSDN相關章節。
2、配置ASP.NET AJAX Profile Service
檢視ASP.NET AJAX提供的配置檔案,在<configSections />可以看到如下定義:
<configSections>
<sectionGroup name="microsoft.web" type="Microsoft.Web.Configuration.MicrosoftWebSectionGroup, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="scripting" type="Microsoft.Web.Configuration.ScriptingSectionGroup, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<sectionGroup name="webServices" type="Microsoft.Web.Configuration.ScriptingWebServicesSectionGroup, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<section name="jsonSerialization" type="Microsoft.Web.Configuration.ScriptingJsonSerializationSection, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
<section name="profileService" type="Microsoft.Web.Configuration.ScriptingProfileServiceSection, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
<section name="authenticationService" type="Microsoft.Web.Configuration.ScriptingAuthenticationServiceSection, Microsoft.Web.Extensions, Version=1.0.61025.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="false" />
</sectionGroup>
</sectionGroup>
</sectionGroup>
</configSections>
請注意加粗的部分,它說明在web.config中,我們可以配置Profile Service的一些屬性。根據分析代碼(事實上從web.config的注釋中也能得察一二),可以得到配置的使用方式。例如:
<profileService enabled="true"
writeAccessProperties="ZipCode, Address.Street"
readAccessProperties="Address.State" />
如果将enabled設為false,則在使用Profile Service時會抛出異常。writeAccessProperties和readAccessProperties屬性(在這裡感謝Cat Chen的提醒,請注意在Beta1版本中,web.config中的注釋是錯誤的)分别定義了可以使用Profile Service設定和擷取的屬性清單。由于System.Web.UI.WebControls.StringArrayConverter的作用,能夠使逗号分割的字元串與字元串數組之間進行轉換。另外,能夠使用“.”來表述Group中的某個Profile資訊,例如“Address.State”則表示了Address這個Group下的State資訊。
請注意,雖然定義了writeAccessProperties和readAccessProperties,但是從用戶端設法設定或讀取“不被允許”的Profile屬性也不會出現錯誤,隻是設定或者讀取無法成功而已。
3、使用Profile Service讀取Profile資訊
從用戶端擷取Profile資訊的主要邏輯如下:
調用Sys.Services.ProfileService.load(propertyNames, loadCompletedCallback, failedCallback, userContext)。
如果propertyNames為空,則調用伺服器端GetAllPropertiesForCurrentUser方法,否則使用_clonePropertyNames函數剔出propertyNames中重複的屬性名,然後調用伺服器端_GetPropertiesForCurrentUser方法。
伺服器端根據得到的參數,從Profile資訊中提取資訊并構造出一個Dictionary<string, object>數組。
如果成功,則根據伺服器端傳回對象,調用_unflattenProperties方法将該對象轉換為Profile屬性以及Profile Group,最後調用loadCompletedCallback回調函數。
如果失敗,調用failedCallback函數。
需要注意的是,如果需要加載所有的Profile,在第1步中則必須使用null作為第一個參數,如果使用了一個空數組,則不會加載任何Profile屬性。
關于第2步中的方法通路,ASP.NET AJAX中的Application Services都是基于ASP.NET AJAX的用戶端Web Services通路能力的。在預設情況下Profile Service會通路“ScriptServices/Microsoft/Web/Profile/ProfileService.asmx”,由于不存在這個檔案,ASP.NET AJAX将會查找Microsoft.Web.Profile.ProfileService類并調用該類的方法,這個就是“Assembly-based Web Services Access”。不過令人扼腕的是,雖然在目前版本的ASP.NET AJAX中還保留了這個機制,但是根據“白皮書”和可靠來源基本上可以确定這個功能會在下一個版本中被移除。這讓我感到非常不可思議。個人認為,ASP.NET AJAX的一個重要特點,就是提供了一個能夠擴充的模型。對于用戶端來說,無論Control,Action,Validator乃至Behavior,幾乎方方面面都能有規可循地進行擴充。很自然,伺服器端也是,可能ASP.NET AJAX伺服器端中最典型的應用就是Control Tookit了。其良好的複用性讓人可謂眼前一亮。既然封裝成了一個元件,則分發和部署則成為了一件重要的事情。許多ASP.NET AJAX元件必須通過用戶端和伺服器端的配合,然後使用用戶端通路伺服器端的Web Services方法來執行。如果需要在程式集中進行輸出Javascript,則可以使用内嵌資源。但是如果用戶端需要通路的Web Services檔案是獨立于程式集之外的話,那麼還是增加了分發和部署的難度,甚至在開發與維護方面也變得需要管理多個對象,非常麻煩。而“Assembly-based Web Services Access”就能很好地解決這個問題。這樣優秀的特性為什麼會被取消?
在第4步中,從伺服器端得到的資料是以如下JSON形式出現的:
"ZipCode" : "...",
"Address.City" : "...",
"Address.State" : "..."
Profile Group也是通過“.”分割來表示的。很自然,如果在load方法的第一個參數需要傳遞一個Profile Group中的一個屬性時,也需要使用這樣的表示方法。
另外,loadCompletedCallback和failedCallback函數的簽名分别如下:
// propertyNumber:成功加載的Profile數量,同一個Profile Group下的屬性會分别計算
// userContext:調用load時傳入的userContext
// methodName:方法名,值為"Sys.Services.ProfileService.load"
function loadCompletedCallback(propertyNumber, userContext, methodName)
...
// error:錯誤對象,存放了錯誤資訊
function failedCallback(error, userContext, methodName)
事實上,這兩個回調函數都可以在調用load時不指定,這樣就會使用預設的回調函數和預設的userContext,它們可以通過以下方法設定:
Sys.Services.ProfileService.set_loadCompleteCallback(callback);
Sys.Services.ProfileService.set_defaultFailedCallback(callback);
在load方法調用成功後,就可以在用戶端獲得使用Profile的值了,例如:
var zipCode = Sys.Services.ProfileService.properties.ZipCode;
var city = Sys.Services.ProfileService.properties.Address.City;
var state = Sys.Services.ProfileService.properties.Address.State;
4、使用Profile Service設定Profile資訊
從用戶端設定Profile資訊的主要邏輯如下:
調用Sys.Services.ProfileService.save(propertyNames, saveCompletedCallback, failedCallback, userContext) 。
調用_flattenProperties,根據propertyNames将Sys.Services.ProfileService.properties中的資訊變為JSON形式。如果propertyNames為null,則表示儲存所有Profile屬性。
調用伺服器端SetPropertiesForCurrentUser方法,伺服器端會将JSON字元串轉變為IDictionary<string, object>,并儲存在Profile中。
如果成功,則調用saveCompletedCallback回調函數。
如果失敗,則調用failedCallback回調函數。
在第2步中,如果需要表示Profile Group中的屬性,依舊通過“.”來分割。如果需要修改Profile資訊,直接修改properties對象即可。如果需要建立Profile Group,則需要使用到用戶端的Sys.Services.ProfileGroup類。例如:
Sys.Services.ProfileService.properties.ZipCode = "...";
if(!Sys.Services.ProfileService.properties.Address)
Sys.Services.ProfileService.properties.Address = new Sys.Services.ProfileGroup();
Sys.Services.ProfileService.properties.Address.City = "...";
如果某Profile屬性為複雜類型,則使用JSON形式在用戶端指派即可。
至于回調函數的簽名,和load一模一樣,隻是methodName的值變為了"Sys.Services.ProfileService.save"。
在這篇文章中,我們簡單讨論了ASP.NET AJAX的使用方法。在下一篇文章中,我們将一起來看一下配合ScriptManager來自定義Profile Service。