Mountainnon Pahl
Microsoft Corporation
2002 年 4 月
摘要:提供 Microsoft .NET 和 COM+ 服務整合背後的技術詳細資料,並說明 Managed 程式碼可用的服務。 (26 個列印的頁面)
目錄
簡介
交易
部署
服務元件
物件存留期
安全性
遠端元件
結論
簡介
本文需要熟悉 Microsoft®.NET Framework和 COM+ 服務。 熟悉企業服務並非必要,但會很有説明。 如需這些主題的良好背景,請參閱:
- Derek Beyer 的書籍 C# COM+ 程式 設計 (專業 Mindware,2001)
- TimE文章 COM+ 整合:.NET 企業服務如何協助您建置分散式應用程式
- Jeffrey Richter 的文章第2 部分:Microsoft .NET Framework提供整合式、Service-Oriented Web 的平臺
COM 提供一種方式來撰寫以元件為基礎的應用程式。 已知寫入 COM 元件所需的管管工作相當重要且重複。 COM+ 對於新版 COM 並不多;相反地,COM+ 提供元件的服務基礎結構。 元件會建置並安裝在 COM+ 應用程式中,以便建置可調整的伺服器應用程式,以輕鬆部署達到高輸送量。 (如果元件不需要使用任何服務,則不應該放在 COM+ 應用程式) 。 藉由從頭設計應用程式來利用交易、物件共用和活動語意等服務,可達到延展性和輸送量。
.NET Framework提供另一種方式來撰寫元件型應用程式,並具有較佳工具支援的 COM 程式設計模型的優點、Common Language Runtime (CLR) ,以及更容易撰寫程式碼語法。 COM+ 服務基礎結構可以從 Managed 和 Unmanaged 程式碼存取。 Unmanaged 程式碼中的服務稱為 COM+ 服務。 在 .NET 中,這些服務稱為企業服務。 從 ServicedComponent 衍生類別表示元件需要服務。 (如果元件不需要使用任何服務,則它不應該衍生自 ServicedComponent) 。 工具支援已改善,可讓程式設計人員撰寫伺服器型應用程式,但延展性和輸送量的相同問題仍在良好的程式設計實務領域。 服務背後的基本概念是從頭開始進行輸送量和延展性的,並利用企業服務,在適當的情況下輕鬆實作這些設計模式。
服務基礎結構設計實際上幾乎與 COM 或甚至元件有關:COM+ 服務現在可以套用至 COM 元件、.NET 元件,甚至是其他未被視為元件的實體,例如 ASP 頁面或任意程式碼區塊, (在 Microsoft Windows® XP 上看到沒有元件 COM+ 功能的服務) 。
目前提供的所有 COM+ 服務都可供 .NET 和 COM 物件使用。 其中一些服務包括:交易、物件共用和建構字串、JIT、同步處理、角色型安全性、CRM 和 BYOT。 如需 Microsoft Windows 2000 上服務的完整清單,請參閱平臺 SDK 中的 COM+ 所提供的服務 。 Microsoft Windows XP 包含新版本的 COM+,也就是 COM+ 1.5,其具有其他服務,也可以與 .NET 元件搭配使用。
交易
若要撰寫使用服務的受控應用程式,需要服務的類別必須衍生自 ServicedComponent,並使用各種自訂屬性來指定所需的實際服務。 本節介紹這些概念,以及它們如何影響撰寫 Managed 程式碼。 稍後小節會提供更詳細的說明。
假設已撰寫類別 Account, (實際程式碼會在稍後) 列出,且位於 BankComponent 元件中。 此類別可以使用,如下所示:
BankComponent 用戶端
using system;
using BankComponent;
namespace BankComponentClient
{
class Client
{
public static int Main()
{
Account act = new Account();
act.Post(5, 100);
act.Dispose();
return 0;
}
}
}
若要建置用戶端,必須將參考新增至 BankComponent 命名空間。 此外,必須在 BankComponentClient 命名空間中新增 System.EnterpriseServices 元件的參考,用戶端會呼叫 Dispose () 和 ServicedComponent 建構函式,這是 System.EnterpriseServices 中定義的方法,而不是包含 BankComponent 的元件。 當衍生類別未覆寫所有基類方法時,這是一般 .NET 需求。
BankComponent Server 程式碼會顯示 .NET 中 Account 類別的實作,其使用交易。 類別 Account 衍生自 System.EnterpriseServices.ServicedComponent類別。 Transaction屬性會將 類別標示為需要交易。 同步處理和 JIT 服務會自動設定,因為使用 Transaction 屬性。 AutoComplete屬性是用來指定執行時間必須在方法執行期間擲回未處理的例外狀況時,自動呼叫交易的SetAbort函式;否則會呼叫SetComplete 函式。 ApplicationName屬性會將此元件與 COM+ 應用程式建立關聯,以儲存此應用程式的服務組態資料。 此類別所需的進一步修改會在程式碼中反白顯示。
BankComponent 伺服器
using System.EnterpriseServices;
[assembly: ApplicationName("BankComponent")]
[assembly: AssemblyKeyFileAttribute("Demos.snk")]
namespace BankComponentServer
{
[Transaction(TransactionOption.Required)]
public class Account : ServicedComponent
{
[AutoComplete]
public bool Post(int accountNum, double amount)
{
// Updates the database, no need to call SetComplete.
// Calls SetComplete automatically if no exception is thrown.
}
}
}
BankComponent 伺服器命名空間中的程式碼示範在 .NET 中使用 COM+ 服務有多容易。 從程式碼撰寫到部署的完整程式摘要如下:
寫入伺服器元件。
建置元件:
簽署元件。 專案可以產生一次金鑰檔。 不需要為每個編譯產生一個。 您可以使用 Microsoft .NET 命令提示字元來建立金鑰,並sn.exe:
sn –k Demos.snk編譯程式碼。 必須新增 System.EnterpriseServices 的參考。
部署應用程式。
使用服務元件的元件必須向 COM+ 目錄註冊。 ServicedComponent 類別和自訂屬性是從 Managed 程式碼取得 COM+ 服務的兩個重要概念。 服務的組態會儲存在 COM+ 目錄中。 物件位於 CLR 內並執行。 Managed 物件及其相關聯的 COM+ 內容會在圖 1 中描述,接下來的兩節將會更清楚。
.gif)
圖 1. 與受控元件相關聯的服務
使用 COM+ 元件時,您必須手動設定目錄,但使用服務元件時,目錄可以根據程式碼中的屬性進行更新。 元件可以使用命令列工具regsvcs.exe或撰寫存取受控 API 的腳本來明確註冊。 以下的部署詳細資料一節會提供更多詳細資料。 在開發期間,只要將元件複製到應用程式目錄,即可方便使用 XCopy 部署。 每當用戶端應用程式建立衍生自 ServicedComponent 的類別實例時,執行時間就會偵測它是否已在 COM+ 應用程式中註冊元件。 如果尚未註冊,則會搜尋本機目錄來尋找元件,如果找到的話,元件中的所有服務元件都會在 COM+ 應用程式中註冊,然後可以繼續啟用。 這稱為延遲註冊,但不適用於所有案例。 例如,標示為 COM+ 伺服器應用程式的元件需要明確註冊 (請參閱下列) ,而延遲註冊不適用於呼叫受控服務元件的 Unmanaged 用戶端。 延遲註冊在開發期間很有用,否則請使用腳本、程式碼或 RegSvcs 來註冊元件。
可能將元件放在 GAC 中。 如需詳細資訊,請參閱部署一節。
執行該用戶端。
部署
自訂屬性是從 Managed 程式碼存取 COM+ 服務的兩個重要概念之一。 自訂屬性可用來指定所需的服務,例如先前程式代碼清單中的 Transaction 自訂屬性。 這些屬性會將服務的組態選項儲存在元件中繼資料中。 自訂屬性是藉由讓某些程式碼載入元件並使用反映,在 屬性上建立屬性的實例和呼叫方法,以擷取儲存在 屬性中的服務組態。 然後,資訊可以寫入 COM+ 目錄。 執行這些和其他步驟的程式碼包含在 EnterpriseServices.RegistrationHelper 中。 為了簡化註冊程式,所有形式的註冊都會使用 EnterpriseServices.RegistrationHelper 元件。 此元件可以當做 Managed 類別以及 COM 物件存取。
.gif)
圖 2. 註冊服務元件
在概念上,RegistrationHelper 會執行下列步驟:
- 使用 RegistrationServices.RegisterAssembly 在登錄中註冊元件。 因此,類別會在登錄中顯示為以 Managed 程式碼撰寫的 COM 元件,並具有指向 mscoree.dll 的 InprocServer32 索引鍵。 如果 Managed 類別未實作任何介面,除非使用 ClassInterfaceAttribute,否則類別的公用方法不會出現在 COM+ 目錄中。 這表示與方法層級相關聯的服務組態無法儲存在目錄中。 不過,某些 COM+ 服務可以在方法層級設定,並要求元件公開介面,如 COM+ 目錄中檢視。 例如,方法層級上的 COM+ 角色型安全性需要元件來實作介面,才能設定服務。 在安全性一節中,會進一步討論此問題。
- 使用 TypeLibConverter 從元件產生 COM 型別程式庫。 ConvertAssemblyToTypeLib。
- 註冊型別程式庫。 到目前為止,這與 RegAsm.exe /tlb 非常相同。
- 尋找或建立 COM+ 應用程式。 名稱是從 ApplicationName 屬性、元件名稱或提供的應用程式名稱/GUID 擷取。
- 使用類型庫,使用 COM+ 系統管理員 API 來設定 COM+ 應用程式。
- 通過所有自訂屬性,並使用 IConfigurationAttribute 將特定服務的組態資料寫入 COM+ 目錄。
RegistrationHelper 會嘗試使用 RegistrationHelperTx 在交易內執行這些步驟,這是安裝 .NET 時所建立 COM+ 應用程式內的類別。 因此,如果註冊失敗,COM+ 目錄和登錄將會還原至其原始狀態。 目前,如果元件位於 GAC) 中,產生的類型程式庫仍會保留在磁片 (或 GAC 中。 如果註冊的元件參考其他也使用 COM+ 服務的元件,則相依性圖形中的所有元件都會經歷上述相同的步驟。
由於 RegistrationHelper 會存取 COM+ 目錄,因此需要電腦上的非受控程式碼許可權和系統管理員許可權。 因此,註冊Helper 的用戶端也是如此:延遲註冊、RegSvcs 或腳本/程式碼。 這也表示無法註冊從網際網路下載或儲存在網路共用上的程式碼。
可以撰寫不相容屬性組合的程式碼,例如要求交易和將同步處理設定為停用。 在將組合寫入 COM+ 目錄時,目前會在註冊期間偵測到這些組合,而不是在編譯期間偵測到。 某些屬性與其他屬性具有隱含相依性,例如,僅使用 Transaction 屬性時,這相當於使用 Transaction、JustInTimeActivation 和 Synchronization 屬性。 註冊 Managed 元件時,除非使用屬性覆寫 'unconfigured' 預設值,否則會使用 COM+ 目錄預設值。 例如,如果元件已註冊且未指定 Transaction 屬性,目錄中交易設定的未設定預設值會設定為 TransactionOption.Disabled。 此方法可讓開發人員移除程式碼中的屬性,如果元件不再需要它,然後在再次註冊元件時,交易的類別目錄專案會適當地重設。 線上檔中會指定這些未設定預設值的詳細清單。 預設值是屬性參數中的預設值,例如,只使用屬性 [Transaction] 表示 TransactionOption.Required。
由於 Managed 類別上的服務組態資料會儲存在 COM+ 目錄中,因此在註冊元件之後,某些目錄專案也可能會進行系統管理修改。 某些服務不應以此方式修改。 例如,停用目錄中的交易服務可能會導致程式碼運作不正確。 您可以在註冊後操作部署特定的設定,例如物件建構字串和安全性角色。 建立註冊後設定時,包含服務元件的元件 XCopy 部署可能不夠。 COM+ 應用程式匯入和匯出功能有助於散發應用程式的目前狀態。 遠端一節會提供有關匯入和匯出的進一步資訊。
在某些情況下,目錄不會查閱組態資料,而是單純從元件中繼資料擷取。 案例適用于 AutoComplete、JIT、物件共用 (,不過集區大小是從目錄擷取) 和安全方法屬性。 如需此問題的詳細資訊,請參閱描述這些服務的各節。
註冊元件的程式會自動產生 COM+ 所需的 GUID。 如果未簽署元件,則只會根據類型和命名空間名稱產生 GUID。 因此,如果未簽署元件,則可以產生非唯一 GUID。 同樣地,也會對甚至不使用 COM+ 服務的 .NET 元件加總,但需要唯一的類型名稱。 因此,必須使用 COM+ 服務的 ssemblies 進行簽署。 如果未簽署元件,註冊將會失敗。 註冊也表示使用 COM+ 服務的 .NET 類別具有一個全域設定資料存放區。 雖然可以將私人元件複製到多個應用程式目錄,但所有這類應用程式最終都會參考服務元件的一個組態資料。 因此,變更 COM+ 目錄中的組態資料會影響所有使用 類別的應用程式。 這在 Microsoft ASP.NET 組態中具有多個 vroot,這全都構成使用服務元件的相同元件複本。 在 Microsoft Windows .NET 上使用 COM+ 分割區的其中一種方式是讓相同 COM+ 應用程式有多個組態。 若要在 .NET 中使用 COM+ 資料分割服務,請勿使用 ApplicationID 屬性,若要在多個分割區中安裝相同的元件,COM+ 需要唯一的應用程式識別碼。
一般而言,每當用戶端需要存取與用戶端應用程式目錄不在相同目錄中的元件,或元件載入另一個進程時,就會使用 GAC,而該元件不在用戶端的目錄中。 在概念上,使用服務元件的私用元件實際上是共用的元件,其使用其共用的組態資料。 如果 ApplicationActivationOption 設定為程式庫,就可以在元件中的類別上使用交易,並在一個用戶端中使用該元件,如果所有元件都是從相同的目錄載入。 當使用 ApplicationActivationOption 的元件設定為伺服器時,元件會由dllhost.exe載入,這很可能不在與用戶端相同的目錄中。 在 COM+ 伺服器應用程式中使用服務元件的元件應該放在 GAC 中。 除非元件位於不同的目錄) ,否則在 COM+ 程式庫中使用服務元件的元件可能不需要放在 GAC (。 唯一的例外狀況是使用 ASP.NET 裝載—元件不應該放在 GAC 中,讓陰影複製能夠正確運作。
若要移除使用服務元件的 .NET 應用程式,請從 GAC (移除元件,如果元件已註冊 GAC) ,請使用 regsvcs.exe從 COM+ 取消註冊元件,然後刪除元件和相關聯的型別程式庫。
版本控制
您可以使用 GUID 屬性來修正 COM+ 所需的 GUID。 不過,建議您使用版本控制,而不是明確使用 GUID。 每當建立新的方法簽章或使用不同服務屬性裝飾類別時,元件的主要或次要版本號碼應該遞增。 註冊應該針對特定版本執行一次。 註冊新版本的元件時,會針對該版本產生新的 GUID,而且元件會使用相同的元件名稱在相同的 COM+ 應用程式中註冊。 因此,元件會在 COM+ 應用程式中出現多次。 不過,每個元件都有 GUID 所提供的唯一識別碼。 每個實例都參考特定版本的元件。 使用 Microsoft Visual Studio® .NET 建置 .NET 應用程式時,通常會注意到這點。 環境會將屬性 [assembly: AssemblyVersion (「1.0.*」) ] 新增至專案。 專案的每個新組建都會產生新的組建編號,因此在重新註冊元件時會產生新的 GUID。 因此, 最好適當地手動遞增組建編號。 用戶端會使用 CLR 版本原則系結至元件,因此將會使用 COM+ 應用程式中的正確類別版本。 在使用服務元件) 撰寫元件 (受控伺服器時,有一些並存案例: (下列使用的某些啟用層面,將在下一節中說明)
- Managed 用戶端、受控伺服器,元件中未使用任何固定 GUID。
- 用戶端會載入版本原則所指定的元件。
- 受控用戶端、受控伺服器、使用的固定 GUID。
- 如果用戶端啟動類別並使用版本原則來取得舊版的元件,程式碼中的固定 GUID 將會在啟用期間使用,以從類別目錄擷取服務資訊。 因此,使用此 GUID 的最後一個已註冊元件的資訊將用來建立 物件,事實上可能是較新的版本,因此嘗試從實際建立的物件轉換時會有類型轉換例外狀況, (v2) 至程式碼中的參考 (v1) 。
- 受管理的用戶端、受控伺服器、沒有固定的 GUID,只會變更組建編號。
- 雖然會產生新的 GUID,但型別程式庫仍會有相同的版本號碼,因為型別程式庫對於版本只有兩個數字。 這仍可能正常運作,但如果版本 2 已安裝于第 1 版,則會卸載第 1 版,則會取消註冊第 2 版的型別程式庫。 解決方案 1:下一版的 .NET Framework (V1.1) 可讓型別程式庫獨立于元件進行版本設定,以解決此問題。 這表示變更元件版本號碼時,應該變更型別程式庫版本。 解決方案 2:僅使用主要和次要版本號碼。
- Unmanaged 用戶端、受控伺服器、未使用固定 GUID。
- 用戶端會使用 GUID 來建立元件。 Interop 會將 GUID 解析為名稱,然後套用版本原則。 如果元件第 1 版和第 2 版位於電腦上,且原則用來取得第 2 版,則 Unmanaged 用戶端會取得第 2 版。
- 安裝第 1 版,安裝第 2 版,卸載第 1 版。 現在,除非有版本原則重新導向至第 2 版,否則用戶端無法建立元件。 此外,第 1 版註冊資訊必須存在登錄專案。 建立卸載第 1 版登錄資訊的其中一種方式是使用 Windows XP 上的 COM+ 別名功能。
版本設定適用于相同 COM+ 應用程式中的所有元件,也就是說,沒有任何自動方式可以設定應用程式本身的版本。 例如,應用程式上的角色無法使用版本原則來設定版本。 使用應用程式名稱屬性來執行應用程式版本設定。
服務元件
啟用
企業服務基礎結構是以內容的概念為基礎。 內容是具有類似執行需求之物件的環境。 在啟用和/或方法呼叫攔截期間,可以強制執行服務。 雖然 COM+ 服務是以 Unmanaged 程式碼撰寫,但 COM+ 服務與 .NET 的整合比在 .NET 中使用 COM Interop 技術更深入。 如果沒有衍生自 ServicedComponent,註冊程式就不會有所需的效果。
服務元件可以啟用並裝載各種組合。 如圖 3 所述,此討論將參考三個案例:同進程 (相同的應用程式域) 、跨應用程式網域 (相同的進程) 和跨進程啟用。 這些案例的重要性是呼叫元件時所跨越的界限。 同進程啟用會導致潛在的跨內容界限,跨應用程式域案例同時具有跨內容和跨應用程式域界限,而跨進程案例則會處理跨電腦/跨進程和跨內容界限。
.gif)
圖 3. 服務元件的啟用主機
Serviced 元件的實作依賴 .NET 遠端處理,其提供可延伸的機制來插入以 Unmanaged 或 Managed 程式碼撰寫的服務。 Serviced 元件衍生自 CoNtextBoundObject 並實作各種介面,例如 IDisposable。 使用 ProxyAttribute 衍生的自訂屬性,即可輕鬆地自訂 CLR 中的啟用鏈結。 您可以藉由撰寫自訂的實際 Proxy 來自訂攔截。 需要新的 Serviced 元件衍生類別時,會自訂啟用鏈結,讓啟用呼叫實際呼叫 CoCreateInstance 的 Managed C++ 包裝函式。 這可讓 COM+ 根據先前註冊元件儲存在 COM+ 目錄中的資訊,設定 Unmanaged 內容和服務。 這也是實作延遲註冊的階段。 在元件註冊期間,InprocServer32 索引鍵會指向 mscoree.dll,因此將 COM+ CreateInstance 重新導向回執行時間,以建立實際的 Managed 物件。 因此,在啟用期間會建立自訂的實際 Proxy 物件。 此 Proxy 的同進程版本稱為服務元件 Proxy 或 SCP。 如圖 4 所示。
.gif)
圖 4. 啟用路徑
啟用呼叫的傳回路徑會透過 Unmanaged COM+ 封送處理 Managed 程式碼的 Managed 參考,然後回到 Managed 程式碼, (圖 4) 中第 1 行的反向路徑。 根據實際物件建立的位置,用戶端會將參考解除封送處理成相關形式。 在同進程啟用的情況下,圖 5 指出參考未封存為透明 Proxy 的直接參考, (TP) 。 跨應用程式域參考未封存為 .NET 遠端 Proxy。 跨進程或跨電腦參考 (圖 6) 需要更多未封存:COM Interop 會在啟用和取消封存期間對 ServicedComponent 所實作的 IManagedObject 進行呼叫。 遠端服務元件 Proxy (RSCP) 在啟用期間對 IServicedComponentInfo 進行呼叫,以取得伺服器物件的 URI,這表示在啟用期間會進行兩個遠端呼叫。 當方法層級需要 COM+ 角色型安全性時,這些介面必須與角色相關聯,當基礎結構在這些介面上進行呼叫時,取消群組才能成功。 安全性一節將討論跨進程啟用和封送處理對於設定角色型安全性的影響。
.gif)
圖 5. 進程呼叫的基礎結構
.gif)
圖 6. 進程外呼叫的基礎結構
因此,已自訂啟用鏈結來建立自訂的實際 Proxy (用於攔截) ,並建立 Unmanaged 內容,只讓 COM+ 具有執行攔截服務語意所需的內容基礎結構。 COM+ 內容現在與 Managed 物件相關聯,而不是 COM 物件。
Interception
圖 7 描述內建方法呼叫基礎結構。 自訂 Proxy (SCP) 可讓 Managed 呼叫被攔截。 在啟用期間,COM+ 內容識別碼會儲存在 SCP 中。 當一個 Managed 物件呼叫服務元件時,儲存在目標 SCP 中的內容識別碼會與目前內容的內容識別碼進行比較。 如果內容識別碼相同,則會直接在實際物件上執行呼叫。 當內容識別碼不同時,SCP 會呼叫 COM+ 來切換內容,並轉譯輸入方法呼叫的服務。 針對同進程呼叫,這類似于 AppDomain.DoCallBack,但 AppDomain 為 COM+。 'DoCallBack' 函式會先在圖 7) 中輸入 COM+ (步驟 2,以切換內容並轉譯服務,然後在 SCP 上呼叫回呼函式。 SCP 會執行資料封送處理,並在實際物件上呼叫 方法。 方法結束時,傳回路徑可讓 COM+ 轉譯語意,以在圖 7 中的步驟 5 (步驟 5 轉譯語意) 。 COM+ 僅用於轉譯服務。 資料封送處理和方法呼叫是在 .NET 執行時間*,* 內執行,因此呼叫方法時不需要將 String 之類的類型轉換成 BSTR。 如果 COM Interop 用於進程內呼叫*,則需要資料封送處理*。* 在 Unmanaged 程式碼中轉譯服務的呼叫不是內部呼叫的 COM Interop 呼叫。
.gif)
圖 7. 進程呼叫的基礎結構
靜態方法上的呼叫不會轉送到透明和實際的 Proxy。 因此,靜態方法無法使用攔截服務;而是在用戶端的內容中呼叫它們。 內部方法會在正確的內容內呼叫,這表示針對新交易設定的物件上呼叫內部方法的用戶端將參與新的交易。 不過,由於方法層級服務需要 COM+ 目錄中的介面, (本主題接下來的詳細資訊,而且在安全性) 中,無法針對方法層級服務設定內部方法。 服務可以套用至屬性,但方法層級屬性 (像是 AutoComplete) 必須個別放在 getter 和 setter 方法上。
AutoComplete 屬性是使用交易的便利方式,不需要撰寫任何程式碼來存取服務。 或者,可以使用 CoNtextUtil.SetAbort 或 CoNtextUtil.SetComplete。 在 COM+ 總管中設定方法屬性的核取方塊,即可設定此服務。 不過,Managed 物件不需要實作介面。 這也適用于服務元件。 當方法未在介面上宣告時,無法在註冊時將方法層級服務的組態寫入目錄;組態只能儲存在中繼資料中。 當方法沒有任何介面存在時,當 AutoComplete 屬性存在時,會使用儲存在 IRemoteDispatch.RemoteDispatchAutoDone 上的組態資訊,從 SCP 進行內容切換。 當 AutoComplete 不存在時,會使用 IRemoteDispatch.RemoteDispatchNotAutoDone。 IRemoteDispatch 是由 ServicedComponent 所實作的介面。 非受控用戶端只能呼叫沒有使用 IDispatch 的介面的服務元件 (晚期繫結) ,因此無法強制執行 AutoComplete 語意,因為在此情況下沒有真正的 Proxy。 即使使用介面,AutoComplete 的設定仍由受控用戶端的中繼資料所驅動。 只有在進程外案例中,才會在 RemoteDispatchAutoDone 上進行 DCOM 方法呼叫。 跨進程元件不會使用 DoCallBack 機制,而是可以使用 DCOM 來傳遞呼叫並轉譯服務。 如果方法位於介面上,則會使用 DCOM 呼叫遠端服務元件上的介面方法,否則呼叫會分派至 ServicedComponent 上的 IRemoteDispatch 介面。 這表示甚至透過 DCOM 呼叫 Dispose () 之類的呼叫,稍後會討論其中的影響。
內容
CoNtextUtil 類別可用來存取相關聯的 COM+ 物件內容及其屬性。 這提供與 Unmanaged 程式碼中 CoGetObjectCoNtext 所傳回的物件類似的功能。 與 Serviced 元件相關聯的 Managed 物件內容與相關聯的 Unmanaged 物件內容有不同的用途。 這可藉由撰寫三個 Managed 物件來顯示,其中一個是交易所需的 (作為根) ,另兩個不是衍生自服務元件, (做為子物件,以示範內容敏捷式 Managed 物件) 。 非服務元件的行為就像是支援 Transactions 的服務元件一樣,也就是說,他們可以呼叫資源管理員,並視需要使用 CoNtextUtil.SetAbort。 建立根物件時,會建立相關聯的 Unmanaged 內容,並與目前的執行緒相關聯。 進行對子物件的呼叫時,因為它們未與 Unmanaged 內容相關聯,因此不需要 COM+ 內容變更,因此執行緒仍會維護根目錄的 Unmanaged 內容識別碼。當子物件在資源管理員上呼叫時,資源管理員會接著從執行子物件的執行緒擷取 Unmanaged 內容,也就是根物件的 Unmanaged 內容。 依賴這很危險,而且在未來的版本中,Unmanaged 內容可能會與 Managed 內容合併,因此子物件將會與可能不同的 Managed 內容相關聯;資源管理員不會挑選根物件的內容。 因此,升級至新版本的 .NET 可能會中斷相依于此行為類型的程式碼。
效能結果
在本節中,受控伺服器服務元件解決方案的效能會與 Unmanaged 用戶端/伺服器解決方案進行比較。 下表說明進程內案例。 針對交易設定的 ServicedComponent 是以 C# 撰寫的,其方法只是新增數位。 對應的 C++ 實作用於比較。 此比較顯示 Managed 與 Unmanaged 解決方案之間的差異,而不需執行任何實際工作。 在受控解決方案中,進程內啟用速度大約為 3.5 倍,而當有內容切換時,方法呼叫的成本大約是 2 倍。 不過,在比較需要內容切換的服務元件方法呼叫與不切換的服務元件方法呼叫時,有大約 3 個數量級的差異,表示內建服務元件攔截基礎結構的成功。 針對跨進程解決方案,啟用的成本大約是 2 倍,而跨內容方法呼叫的成本大約是 3 倍。
表 1 顯示使用 Managed 與 Unmanaged 解決方案進行進程內啟用和方法呼叫的調整時間。
表 1. 進程內啟用和方法呼叫
| 受控解決方案 | 非受控解決方案 | |
| 啟用 | 35 | 10 |
| Cross-coNtext-do-nothing 方法呼叫 | 2 | 1 |
| 跨內容-do-work 方法呼叫 | 200 | 100 |
啟用的成本大約比對 'do-nothing' 方法的方法呼叫高一級。 新增一些工作,只是取得 DTC 交易 (但不會使用它) 將啟用和方法呼叫時間層級調整為相同的大小順序。 當方法呼叫只是開啟集區資料庫連接時,方法呼叫工作會接著大於結合啟用和「不執行任何動作」方法呼叫的程度,證明在將實際工作新增至實驗時,服務元件基礎結構的額外負荷是理論上的。
物件存留期
Just-In-Time 啟用
Just-In-Time (JIT) 服務通常不會隔離使用。 它會隱含地與交易服務搭配使用,而且最常與物件共用搭配使用。 不過,此範例有助於醒目提示一些有趣的主題。 在下列程式碼中,會撰寫只使用 JIT 服務的 .NET 類別。
using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("JITDemo")]
namespace Demos
{
[JustInTimeActivation]
public class TestJIT : ServicedComponent
{
public TestJIT()
{ // First to get called
}
[AutoComplete]
public void DoWork ()
{ // Show doneness using ..
// 1. The autocomplete attribute or
// 2. ContextUtil.DeactivateOnReturn = true or
// 3. ContextUtil.SetComplete();
}
public override void Dispose(bool b)
{ // Optionally override this method and do your own
// custom Dispose logic. If b==true, Dispose() was called
// from the client, if false, the GC is cleaning up the object
}
}
}
類別衍生自 ServicedComponent,並使用 JIT 屬性來指出所需的特定服務。 若要覆寫 Unmanaged 程式碼中的 Activate 和 Deactivate 方法,必須有 類別才能實作 IObjectControl 介面。 ServicedComponent 類別具有可覆寫以處理 Activate 和 Deactivate 事件的虛擬方法。 不過,ServicedComponent 或其實際 Proxy SCP 都不會實作 IObjectControl。 相反地,SCP 會在 COM+ 要求 IObjectControl 介面時建立 Proxy 終止。 接著,COM+ 在終止時呼叫會轉送到 ServicedComponent 的虛擬方法。 DeactivateOnReturn 位是使用 方法上的 AutoComplete 屬性來設定,呼叫 CoNtextUtil.SetComplete () 、CoNtextUtil.SetAbort () 或設定 CoNtextUtil.DeactivateOnReturn。 假設在每次方法呼叫期間都設定 DeactivateOnReturn 位,則方法呼叫的順序會是:類別的建構函式 Activate、實際方法呼叫、Deactivate、Dispose (true) ,並在其中存在時最後是類別的完成項。 進行另一個方法呼叫時,會重複相同的序列。 良好的設計做法是只覆寫 Activate 和 Deactivate 方法,以瞭解何時取出物件並放回物件集區。 Activate 和 Deactivate 的其餘邏輯應該放在類別的建構函式和 Dispose (bool) 方法中。 您可以使用下列其中一種方法來設定 DeactivateOnReturn 位:
- 用戶端只會針對單一方法呼叫使用物件的狀態。 在方法的專案上,會建立新的 real 物件並附加至 SCP。 結束 方法時,會停用實際物件,首先呼叫 Dispose (true) 後面接著實際物件完成項。如果有的話。 不過,相關聯的 COM+ 內容、SCP 和 TP 會保持運作。 用戶端程式代碼仍會維護其認為是透明 Proxy) (實際物件的參考。 用戶端在相同參考上進行的下一個方法呼叫會導致建立新的實際物件並附加至 SCP,以便服務方法呼叫 (請參閱物件共用一節,以移除建立新物件) 的需求。 若要停用實際物件,實際物件必須在方法通話結束時表示完成。 這可以使用:
- 類別方法上的 AutoComplete 屬性
- CoNtextUtil 類別的其中一個方法呼叫:DeactivateOnReturn 或 SetComplete
- 用戶端會在相同的物件上呼叫多個方法,而不停用每個方法呼叫之後的物件,方法是在結束方法之前將完成位設定為 false。 例如,界定在表單層級上使用 JIT 的服務元件,並讓兩個表單按鈕在相同的物件實例上呼叫方法,方法是讓 方法明確地將完成位設定為 false。 在某個時間點,完成度位應該設定為 true。 此方法表示用戶端和物件之間存在合約。 這可由用戶端隱含或明確完成:
- 用戶端知道在物件完成時呼叫特定方法,以停用物件。 方法實作會使用選項 1 中的想法。 您可以使用相同的呼叫序列再次呼叫物件參考,這表示將會建立新的實際物件。
- 物件在物件上呼叫 Dispose () 方法時,用戶端會明確終結該物件。 Dispose () 是在 ServicedComponent 上定義的方法,接著會呼叫 Dispose (true) ,如果存在) ,則類別的完成項 (,然後卸載相關聯的 COM+ 內容。 在此情況下,無法在物件參考上進行進一步的方法呼叫。 如果嘗試此狀況,將會擲回例外狀況。 如果有許多用戶端使用相同的物件,則只有在最後一個用戶端使用 物件完成時,才應該呼叫 Dispose () 。 不過,JIT 物件的無狀態本質會引導設計做法,以針對每個用戶端模型的單一實例。
- 物件永遠不會將其完成度位設定為 true,而且用戶端永遠不會呼叫 Dispose () 。 當垃圾收集發生時,會終結實際的物件、Proxy 和內容。 GC 所起始的方法呼叫順序會是 Deactivate、Dispose (false) 然後如果類別存在) ,則類別完成項 (。
所有服務元件都有相關聯的 COM+ 內容,這會儲存為 SCP (或 RSCP 中的參考,在遠端案例中) 。 只有在 GC 發生或用戶端呼叫 Dispose () 時,才會釋放參考。 最好不要依賴 GC 來清除內容:COM+ 內容會保留到一個 OS 控制碼,而某些記憶體可能會延遲釋放這些控制碼,直到 GC 發生為止。 此外,雖然 ServicedComponent 沒有完成項,但 SCP 會實作完成項,這表示 COM+ 內容參考永遠不會在第一次收集時進行垃圾收集。 事實上,當 SCP 上的完成項最終被呼叫時,完成項執行緒仍然不會終結內容,而是會從完成項執行緒中移除終結內容的工作,並放在內部佇列中。 這是因為發現完成項執行緒可以在服務元件快速建立、使用及超出範圍的特定壓力環境中,工作取用完成項執行緒。 相反地,內部執行緒會服務佇列,終結舊的內容。 此外,任何建立新 ServicedComponent 的應用程式執行緒都會先嘗試將專案從佇列中取出,並終結舊的內容。 因此,從用戶端呼叫 Dispose () 會使用用戶端執行緒更快終止 COM+ 內容,並釋放內容取用的控制碼和記憶體資源。 有時候 Dispose () 可能會擲回例外狀況。 其中一種情況是,如果物件位於已中止的非根交易內容中,Dispose () 呼叫可能會觀察到CONTEXT_E_ABORTED例外狀況。 物件共用中會說明另一個案例。
從效能觀點來看,最好不要在 ServicedComponent 衍生類別中實作完成項,而是將此邏輯放在 Dispose (bool) 方法中。 雖然 SCP 會實作完成項,但實際物件的完成項會使用反映來呼叫。
使用 JIT 的良好設計做法是:
- 將自訂啟用和最終化程式碼放在建構函式和 Dispose (bool) 方法中,不要實作完成項並使用單一呼叫模式,方法是在 方法上使用 AutoComplete 屬性來表示完成。
- 當用戶端使用 物件完成時,從用戶端呼叫 Dispose () 。
討論假設用戶端是受控的,而且元件正在進行中。 元件跨進程時: (遠端處理一節會概述更多詳細資料)
- GC 只會在用戶端啟始物件的 .NET 遠端租用時間已過期時清除物件。
- 如先前所述,在跨進程元件上呼叫方法時,會使用 DCOM 來切換內容並傳遞方法呼叫。 如果先前 JIT 已停用元件,然後呼叫 Dispose () ,則會輸入伺服器內容,並重新建立實際物件以服務 DCOM 呼叫,最後再次停用。 對於進程內元件,如果已停用實際物件,則不會嘗試在維護 Dispose () 呼叫之前切換到正確的內容, (重新啟用元件) ,而是只會終結內容。
物件共用
物件共用的基本內部部署是物件重複使用。 物件共用最常與 JIT 搭配使用。 這適用于集區 COM 元件和集區 .NET 元件。
using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("OPDemo")]
namespace Demos
{
[ObjectPooling(MinPoolSize=2, MaxPoolSize=50, CreationTimeOut=20)]
[JustInTimeActivation]
public class DbAccount : ServicedComponent
{
[AutoComplete]
public bool Perform ()
{ // Do something
}
public override void Activate()
{ // .. handle the Activate message
}
public override void Deactivate()
{ // .. handle the Deactivate message
}
public override bool CanBePooled()
{ // .. handle the CanBe Pooled message
// The base implementation returns false
return true;
}
}
}
如同使用 JIT 時的情況,可以使用兩種方式之一來使用物件共用:
- 單一呼叫模式。 在程式碼中,當用戶端嘗試進行方法呼叫時,會從集區擷取 物件,並在結束時從單一方法呼叫傳回集區,假設 JIT 與物件共用搭配使用,且完成度位會在方法呼叫期間設定為 true。 使用 JIT 的相同單一呼叫方法也適用于這裡。 只有在物件建立並放置在集區中時,才會呼叫建構函式一次。 使用 JIT 和集區物件時的方法呼叫順序為:Activate、方法呼叫、Deactivate 和 CanBePooled。 如果 CanBePooled 傳回 true,則會將物件放回集區 (,但內容仍會保持運作,如先前) 所述。 在從集區擷取任意物件之後, (服務元件無法使用參數化建) 構函式) 之後, (對後續方法呼叫重複相同的方法呼叫順序 (。 最後,如果用戶端在集區物件上呼叫 Dispose () ,則只會在進程案例中終結內容。 如先前所述,在跨進程案例中,Dispose () 的呼叫可以重新啟始物件。 如果物件已集區化,則必須從集區取得一個物件,這表示 Dispose () 可以擲回具有CO_E_ACTI加值稅ION_TIMEOUT的例外狀況。
- 多重呼叫模式。 使用 JIT 服務中醒目提示的類似多個方法呼叫方法,物件只能在物件上的數個方法呼叫之後放回集區。 不過,如果用戶端未呼叫 Dispose 且未使用 JIT,則無法確保當物件由 GC 放回集區時,可以重新建立需要最終處理的集區物件子物件。 當集區物件被垃圾收集時,在 [停用] 中也不會保證成員仍然有效。 在下一個版本的 .NET Framework (V1.1) 中,不會呼叫 canBePooled 和 Deactivate,而且物件不會放回集區。 使用此方法時,有更一致的模型—在停用子物件運作中,在 Dispose () 中,子物件不保證能夠運作。 因此,Dispose () 對於未使用 JIT 的集區物件呼叫非常重要,否則不會將物件傳回集區。
系統管理員在部署和註冊元件之後,可以接受修改集區大小和逾時。 集區大小變更會在重新開機進程時生效。 在 Windows XP 或更新版本上,集區大小會套用至進程內的每個應用程式域。 在 Windows 2000 上,集區大小是使用位於預設應用程式域的集區物件來處理寬的,這表示如果集區物件需要來自相同進程內另一個應用程式域,用戶端就會有效地跨應用程式域與集區物件通訊。 其中一個實現方式是使用 COM+ 程式庫應用程式中定義的集區 .NET 物件,其位於每個 IIS vroot 存放在個別的應用程式域中的 ASP.NET 應用程式中。
服務元件無法使用參數化建構函式。
安全性
程式碼存取安全性 (CAS)
.NET Framework安全性可讓程式碼只有在具有許可權時才能夠存取資源。 為了表達這一點,.NET Framework會使用許可權的概念,這代表程式碼存取受保護資源的許可權。 程式碼會要求所需的許可權。 .NET Framework提供程式碼存取權限類別。 或者,也可以撰寫自訂許可權類別。 這些許可權可用來向.NET Framework程式碼需要執行哪些動作,以及指出程式碼的呼叫端必須有權執行的動作。 透過 System.EnterpriseServices 的任何程式碼路徑要求 Unmanaged 程式碼許可權。
.NET 中的程式碼存取安全性最適用于從 Web 下載程式代碼且作者可能不完全信任的應用程式。 一般而言,使用服務元件的應用程式完全信任,需要安全性才能在多個進程之間流動,並在部署時啟用角色的設定。 這些是 COM+ 角色型安全性公開的功能。
透過 System.EnterpriseServices 的任何程式碼路徑都需要 Unmanaged 程式碼許可權。 這具有如下表示:
- 需要非受控程式碼許可權,才能在服務元件上啟用和執行跨內容呼叫。
- 如果服務元件的參考傳遞至不受信任的程式碼,則無法從不受信任的程式碼呼叫 ServicedComponent 上定義的方法。 不過,在某些情況下,衍生自 ServicedComponent 的類別上定義的自訂方法可能會從不受信任的程式碼呼叫:從不受信任的程式碼呼叫可以在不需要內容切換的自訂方法上進行呼叫、攔截服務,以及方法的實作不會呼叫 System.EnterpriseServices 的成員。
此外,在 .NET 第 1 版中,當執行緒交換器建立時,不會複製安全性堆疊,因此不應該在服務元件內使用自訂安全性許可權。
Role-Based安全性 (RBS)
System.EnterpriseServices 可為 .NET 物件提供安全性服務,以鏡像 COM+ 安全性機制的功能。 當 COM+ 伺服器應用程式用來裝載元件時,RBS 功能會要求 DCOM 傳輸通訊協定是用來從遠端用戶端啟動元件。 下一節會提供有關遠端處理的詳細資訊。 因此,COM+ 中的安全性呼叫內容和身分識別可供 Managed 程式碼使用。 此外,CoImpersonateClient、CoInitializeSecurity 和 CoRevertClient 是一般在伺服器端使用的熟悉呼叫,而 CoSetProxyBlanket 通常用於用戶端。
某些安全性設定不會使用屬性儲存在中繼資料中,例如,將使用者新增至角色,以及設定程式安全性身分識別。 不過,元件層級屬性可用來設定 COM+ 伺服器應用程式的 COM+ 總管安全性索引標籤中顯示的內容:
啟用應用程式 (ApplicationAccessControlAttribute (bool) ) 的授權。 這是支援 RBS 的必要條件。
(ApplicationAccessControlAttribute (AccessChecksLevelOption) ) 的安全性層級。 如果設定為 AccessChecksLevelOption.Application,則會將指派給應用程式中角色的使用者新增至進程安全性描述元,並在元件、方法和介面層級進行更精細的角色檢查已關閉。 因此,安全性檢查只會在應用層級執行,而程式庫應用程式依賴主機進程來取得進程層級安全性。 如果屬性設定為 AccessChecksLevelOption.ApplicationComponent,則指派給應用程式中角色的使用者會新增至進程安全性描述元,並在應用程式上執行角色型安全性檢查。 此外,也必須針對每個需要 RBS 的元件啟用存取檢查,方法是在 類別上套用 ComponentAccessControl 屬性。 在程式庫應用程式中,以角色為基礎的安全性檢查會如同伺服器應用程式一樣執行。 安全性屬性包含在應用程式內所有物件的內容中,且安全性呼叫內容可供使用。 如果物件具有與其建立者內容不相容的組態,則會在自己的內容中啟動。 以程式設計方式的角色型安全性依賴安全性呼叫內容的可用性。
若要讓任何有意義的存取檢查適用于 COM+ 程式庫應用程式,請選擇在程式和元件層級執行存取檢查。
模擬和驗證選取專案會對應至 ApplicationAccessControl 屬性的 ImpersonationLevel 和 Authentication 屬性。
SecurityRole 屬性可以套用至元件、類別或方法層級。 在元件層級套用時,該角色中的使用者可以啟用應用程式中的任何元件。 套用至類別層級時,該角色中的使用者可以呼叫元件上的任何方法。 應用程式和類別層級角色可以在中繼資料中設定,或透過存取 COM+ 目錄以系統管理方式設定。
使用中繼資料在元件層級設定 RBS:
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] // adds NTAuthority\everyone to this role [assembly:SecurityRole("TestRole1",true)] // add users to roles administratively [assembly:SecurityRole("TestRole2")]在中繼資料的類別層級設定 RBS:
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] … [ComponentAccessControl()] [SecurityRole("TestRole2")] public class Foo : ServicedComponent { public void Method1() {} }元件或類別層級的 RBS 可以系統管理方式設定,因為這些實體存在於 COM+ 目錄中,且元件已註冊之後。 不過,如先前所述,類別方法不會出現在 COM+ 目錄中。 若要在方法上設定 RBS,類別必須實作介面的方法,而且必須在類別層級上使用 SecureMethod 屬性,或在方法層級使用 SecureMethod 或 SecurityRole。 此外,屬性必須出現在類別方法實作上,而不是介面定義中的介面方法。
在方法上使用 RBS 的最簡單方式是在類別層級上套用 SecureMethod 屬性,然後設定角色 (系統管理方式或將 SecurityRole 屬性放在方法上) 。
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] Interface IFoo { void Method1(); void Method2(); } [ComponentAccessControl()] [SecureMethod] public class Foo : ServicedComponent, IFoo { // Add roles to this method administratively public void Method1() {} // "RoleX" is added to the catalog for this method SecurityRole("RoleX") public void Method2() {} }在類別層級上使用 SecureMethod 可讓類別中所有介面上的所有方法以系統管理方式設定 COM+ 目錄中的角色。 如果類別會實作兩個介面,每個介面都有相同的方法名稱和角色是系統管理設定的,則必須在兩種方法上設定角色,因為它們出現在 COM+ 目錄 (,除非類別實作特定的方法,例如 IFoo.Method1) 。 不過,如果在類別方法上使用 SecurityRole 屬性,則註冊元件時,具有相同方法名稱的所有方法都會自動使用該角色設定。
SecureMethod 屬性也可以放在方法層級。
[assembly: ApplicationAccessControl(true, AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)] Interface IFoo { void Method1(); void Method2(); } [ComponentAccessControl()] public class Foo : ServicedComponent, IFoo { // Add roles to this method administratively [SecureMethod] // Or use SecurityRole (translates to SecureMethod++) public void Method1() {} public void Method2() {} }在此範例中,IFoo 和這兩種方法都會出現在 COM+ 目錄中,因此可以在任一方法系統管理上設定角色,不過,方法層級 RBS 只會在 Method1 上強制執行。 在參與方法層級 RBS 安全性的所有方法上使用 SecureMethod 或 SecurityRole,或在先前所述的類別層級上放置 SecureMethod。
每當在方法層級上設定 RBS 時,需要封送器角色:在方法上進行方法呼叫且未設定 RBS 時,服務元件基礎結構會在 IRemoteDispatch 上呼叫。 當方法呼叫發生 (,且當 SecureMethod 屬性存在) 時,方法呼叫會使用與 方法相關聯的介面,使用 DCOM 進行方法呼叫。 因此,DCOM 保證在方法層級強制執行 RBS。 不過,如啟用和攔截一節所述,COM Interop 和 RSCP 接著會呼叫 IManagedObject (,讓遠端啟動器將參考封送處理到其空間) 和 IServicedComponentInfo (,以查詢遠端物件) 。 這些介面與服務元件相關聯。 由於元件已設定為執行方法層級檢查,因此如果基礎結構成功進行這些呼叫,就必須與這些介面建立關聯。
因此,在註冊元件時,會將封送器角色新增至應用程式,然後必須以系統管理方式將使用者新增至此角色。 應用程式所有使用者最常新增至此角色。 這與 Unmanaged COM+ 稍有不同,其中在方法上設定 RBS 不需要這個額外的設定步驟。 在註冊期間自動將「所有人」新增至此角色是潛在的安全性漏洞,因為任何人現在都可以啟用 (,但無法呼叫) 元件,在他們可能沒有啟用許可權之前呼叫這些元件。 封送處理器角色也會新增至 IDisposable 介面,以允許用戶端處置物件。 封送器角色的替代方式是讓使用者將相關角色新增至上述三個介面的每一個。
遠端元件
ServicedComponent 類別在其繼承樹狀結構中包含 MarshalByRefObject,因此可以從遠端用戶端存取。 如何從遠端公開服務元件有許多變化。 服務元件可以使用:
- 具有從 ASP.NET 中呼叫或寫入之服務元件的 HTTP 通道提供良好的安全性和加密選項,以及已知的延展性和效能。 搭配 SOAP 使用時,有更多互通性選項存在。 服務元件可以裝載在 IIS/ASP.NET 中,做為 COM+ 程式庫應用程式。 如果使用 COM+ 伺服器應用程式,IIS/ASP.NET 主機可以使用 DCOM 來存取元件。
- COM+ Web 服務會討論將服務元件公開為 SOAP 端點的替代方式 :Check-Box路由至 XML Web 服務。
- 當服務元件裝載于 Dllhost 時,DCOM。 此選項提供最佳的效能和安全性,以及跨電腦傳遞服務內容的能力。 選擇遠端技術時的主要設計問題應該是服務是否需要跨電腦流動。 例如,在伺服器陣列中,在一部機器上建立交易,而且交易必須繼續在另一部電腦上,DCOM 是唯一可用來達成此目的的通訊協定。 不過,如果用戶端只需要呼叫遠端 ServicedComponent,則 HTTP 通道或 SOAP 端點方法是很好的替代方案。
- 例如,TCP 或自訂通道) 的 .NET 遠端通道 (。 若要使用 TCP 通道,您需要接聽通訊端上的進程。 一般而言,自訂程式是用來接聽通訊端,然後將服務元件裝載為 COM+ 程式庫或伺服器應用程式。 或者,Dllhost 可以當做接聽程式使用。 任一種方法都不太可能使用,而且需要撰寫經過實證效能、延展性和安全性的自訂通訊端接聽程式。 因此,ASP.NET 或 DCOM 解決方案是大部分專案的最佳方法。
若要使用 DCOM 從遠端存取服務元件,並裝載于 Dllhost 中,請先確定元件已在 COM+ 伺服器應用程式中註冊,並放置在伺服器電腦上的 GAC 中。 然後,使用 COM+ 應用程式匯出裝置來建立應用程式 Proxy 的 MSI 檔案。 在用戶端上安裝應用程式 Proxy。 內嵌在應用程式 Proxy 中是 Managed 元件。 安裝程式也會註冊元件,並將它放在用戶端電腦上的 GAC 中。 因此:
- .NET Framework必須安裝在用戶端和伺服器上。 即使只有非受控用戶端會存取遠端服務元件,用戶端電腦上也需要這樣做。 在 Windows 2000 平臺上,也需要 Service Pack 3。
- 卸載 Proxy 之後,也必須從 GAC 移除元件。
圖 6 顯示從用戶端在 Managed 程式碼中啟動伺服器元件之後的基礎結構。
使用 DCOM 表示 CLR 裝載在 Dllhost 中,這表示應用程式組態檔dllhost.exe.config位於 system32 目錄中。 這也表示組態檔會套用至電腦上的所有 Dllhost 進程。 在下一版的 .NET Framework (V1.1) 中,COM+ 應用程式根目錄可以在 COM+ 應用程式中設定,而且該目錄可用來探索應用程式的組態檔和元件。
針對用戶端啟動的物件,每當要求物件的 URI 時,就會為該物件建立存留期租用。 如啟用一節稍早所述,遠端服務元件 Proxy 會要求 URI。 當現有的進程內服務元件封送處理至遠端進程時,也可能會發生此情況—每當 .NET 在應用程式域外部封送處理 MBR 物件時,就會要求 URI。 URI 可用來確保 .NET 中的物件識別是唯一的,並防止 Proxy 鏈結。 因此,當 Managed 用戶端啟動遠端服務元件時,會在伺服器物件上使用租用時間。 請注意,非受控用戶端在用戶端上沒有遠端服務元件 Proxy,因此不會要求物件的 URI。 相反地,Unmanaged 用戶端會使用 DCOM 來確保物件識別。 因此,從非受控用戶端啟動服務元件時,不會使用服務元件上的租用時間。
在服務元件涉及租用時間的情況下,最好將 InitialLeaseTime 和 RenewOnCallTime 逾時值設定為較小的值,甚至可能小於 10 秒。 Serviced 元件會使用 Dispose () 終結,或讓 GC 清除物件。 呼叫 Dispose () 時,遠端服務元件 Proxy 會釋放它在 DCOM Proxy 上擁有的參考,然後讓其本身可供下一個 GC 使用。 伺服器物件會處理 Dispose 呼叫 (或建立新的伺服器物件,以服務對 Dispose () ) 的遠端呼叫、終結相關聯的 COM+ 內容,然後讓其本身可供下一個 GC 使用,但只有在租用時間逾時時。當用戶端未呼叫 Dispose () 時,伺服器必須先等候用戶端 GC 釋放對 DCOM Proxy 的參考,然後讓其本身和 COM+ 內容在租用時間過期之後可供下一個 GC 使用。 因此,呼叫 Dispose () ,此外,請減少預設租用時間。 即使用戶端保持運作且租用時間過期,伺服器物件的 DCOM 參考仍會讓伺服器物件保持運作。 不過,DCOM 參考不一定會用來讓服務元件保持運作。 當用戶端透過 CLR 遠端通道或 COM+ SOAP 服務存取物件時,只有由於租用的強式參考會讓服務元件保持運作。
結論
本文只討論一些可供 Managed 程式碼使用的服務。 所有 COM+ 服務都可供 Managed 程式碼使用,例如交易隔離等級、進程初始化、不含元件和進程回收的服務。 .NET Framework現在會以一致且邏輯的方式,提供所有 COM+ 服務的相等存取權。 此外,.NET Framework的一些創新部分,例如 ASP.NET、Microsoft ADO.NET 和傳訊,與 .NET Enterprise Services 深入整合,利用交易和物件共用等服務。 此整合提供一致的架構和程式設計模型。 System.EnterpriseServices 命名空間提供程式設計模型,以將服務新增至 Managed 類別。