天天看點

深入Atlas系列:探究Application Services(1) - Profile Service分析與使用

  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。

繼續閱讀