services|web|xml|複選框|微軟
事務性組件樣本
簡單的計算機遠算不上工作量繁重的商務應用程式,因此我們現在考慮帶有對象池的適於 COM+ 事務性組件的應用程式。
最容易管理和配置的組件是由
ServicedComponent 匯出的Managed 程式碼組件,如以下 C# 樣本所示:
using System;using System.Reflection;using System.Runtime.InteropServices;using System.EnterpriseServices;using System.Data;using System.Data.SqlClient;[assembly: ApplicationName("SCTrans")][assembly: ApplicationActivation(ActivationOption.Server, SoapVRoot="SCTrans")][assembly: AssemblyKeyFile("SCTrans.snk")]namespace SCTrans{ public interface ISCTrans { string CountUp (string Key); } [ObjectPooling(MinPoolSize=0, MaxPoolSize=25)] [JustInTimeActivation(true)] [ClassInterface(ClassInterfaceType.AutoDual)] [TransactionAttribute(TransactionOption.RequiresNew)] public class SCTransSQLNC : ServicedComponent, ISCTrans { [AutoComplete] public string CountUp (string Key) { _command = new SqlCommand("", _connection); _command.CommandType = CommandType.Text; _command.Connection.Open(); _command.CommandText = "UPDATE CallCount WITH (ROWLOCK) SET CallCount = CallCount + 1 WHERE Machine='" + Key + "'"; _command.ExecuteNonQuery(); _command.Connection.Close(); _numcalls++; return (_numcalls + " NC " + _guid); } protected override bool CanBePooled() { return true; } private int _numcalls = 0; private string _guid = Guid.NewGuid().ToString(); private SqlConnection _connection = new SqlConnection("user id=MyUser;password=My!Password; database=SoapTest;server=MyServer"); private SqlCommand _command; }}
要建立並運行此 C# 組件,在完成編輯串連值以串連到 Microsoft SQL Server™ 資料庫之後,需要使用 sn.exe 產生 sctrans.snk 加強式名稱關鍵字檔案,然後在
using 語句中使用程式集引用對其進行編譯。如果您在伺服器上進行部署,應使用 gacutil.exe(如果正在使用 SDK)或通過 .NET 架構使用者介面將程式集放入 GAC,然後運行 regsvcs.exe,註冊 COM+ 託管組件。Regsvcs.exe 將使用以下屬性,將組件發布為伺服器上的 SOAP 端點和伺服器(進程外)啟用:
[assembly: ApplicationActivation(ActivationOption.Server, SoapVRoot="CSSoapSQL")]
此組件在每種方法調用中使用不同的事務,具有一個自動完成方法,並被配置為進行緩衝。使用託管和非託管 COM+ 組件時,對象池和事務將如所預期的那樣通過 SOAP 運行。例如,如果使用下列 VBScript 通過 SOAP 訪問以下
ServicedComponent:
mon = "soap:wsdl=http://jnoss3/sctrans/SCTrans.SCTransSQLNC.soap?WSDL"WScript.Echo(mon)for i = 1 to 2 set c = GetObject(mon) for j = 1 to 10 WScript.Echo i & " " & j & " " & c.CountUp("SCWKONC") nextnext
將顯示以下輸出內容:
C:\moniker>actscwkoMicrosoft (R) Windows Script Host Version 5.6Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.soap:wsdl=http://jnoss3/sctrans/SCTrans.SCTransSQLNC.soap?WSDL1 1 486 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 2 487 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 3 488 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 4 489 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 5 490 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 6 8 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 1 7 9 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 1 8 10 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 1 9 494 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 1 10 495 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 2 1 13 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 2 14 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 3 15 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 4 499 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 2 5 17 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 6 501 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 2 7 502 NC 6e41f32f-74be-45f0-94c0-989e7e1c5672 2 8 19 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 9 20 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce 2 10 21 NC af26b53b-4a1f-48c8-8880-518c2b55a7ce
這就是所預期的緩衝的組件:從緩衝池中拖出對象並重新使用。使用用戶端啟用的緩衝組件的行為都是相同的。
非託管組件的對象池和事務也如所預期的那樣運行(雖然 Visual Basic 6.0 組件不支援對象池)。需要為大多數非託管應用程式通過 COM+ 管理工具設定緩衝和事務屬性。
傳遞引用
WKO 與 CAO 模型的一個關鍵區別在於它們向有狀態的對象傳遞引用的能力。以下是 C#
ServicedComponent 樣本,顯示了此操作的基本步驟:
using System;using System.Reflection;using System.EnterpriseServices;using System.Runtime.InteropServices;[assembly: ApplicationName("RefPass")][assembly: ApplicationActivation(ActivationOption.Server, SoapVRoot="RefPass")][assembly: AssemblyKeyFile("RefPass.snk")]namespace RefPass{ public interface IParent { string SetRef(object inKid); object GetRef(); string CountUp(object obj); } public interface IChild { string GetValue (); string CountUp(); void SetName(string key); } [ClassInterface(ClassInterfaceType.AutoDual)] public class Parent: ServicedComponent, IParent { protected Child _kid = null; public string SetRef(object inKid) { _kid = (Child)inKid; return _kid.GetValue(); } public object GetRef() { return (object)_kid; } public string CountUp(object obj) { Child kid = (Child)obj; if (kid == null) return _kid.CountUp(); else return kid.CountUp(); } } [ClassInterface(ClassInterfaceType.AutoDual)] public class Child : ServicedComponent, IChild { private int _counter = 0; private string _name = "none"; public string CountUp() { _counter++; return GetValue(); } public string GetValue() { return (_name + " " +_counter.ToString()); } public void SetName(string key) { _name = key; } }}
此 C# 程式有兩個類:
Child 和
Parent。如果運行以下 VBScript 樣本,WKO 與 CAO 模型的區別會更加明顯:
set c1 = GetObject ("soap:wsdl=http://jnoss4/refpass/RefPass.Child.soap?wsdl")set c2 = GetObject ("soap:wsdl=http://jnoss4/refpass/RefPass.Child.soap?wsdl")c1.SetName("C1")WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()C2.SetName("C2")WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()
運行時將顯示以下輸出內容:
C:\moniker>refpasswkoMicrosoft (R) Windows Script Host Version 5.6Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.none 1none 1none 1none 1none 1none 1none 1none 1none 1none 1
名稱和值說明了單一調用已知對象的無狀態性質,因為組件是使用不同的方法調用建立的,所以方法調用之間不保留名稱或值。
如果匯出用戶端代理,然後匯入到另一台用戶端電腦上,並且運行了下面的 VBScript,則 SOAP 啟用將是 CAO 而不是 WKO:
'直接建立兩個對象set c1=CreateObject("RefPass.Child")set c2=CreateObject("RefPass.Child")'設定第一個對象的名稱,並調用數次'以遞增對象內部計數器c1.SetName("C1")WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()WScript.Echo c1.Countup()WScript.Echo c1.CountUp()WScript.Echo c1.Countup()'設定第一個對象的名稱,並調用數次'以遞增對象內部計數器c2.SetName("C2")WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()WScript.Echo c2.Countup()WScript.Echo c2.CountUp()WScript.Echo c2.Countup()'建立父物件set p=CreateObject("RefPass.Parent")'將子物件傳遞到父物件,並從父物件調用子物件WScript.Echo p.SetRef(c1)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)'現在調用儲存在父物件內部的子物件dim c9WScript.Echo p.CountUp(c9)'從父物件擷取該對象並直接調用Set c3 = p.GetRef()WScript.Echo c3.CountUp()
從命令列運行時,將顯示以下輸出內容:
C:\moniker>refpassclMicrosoft (R) Windows Script Host Version 5.6Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.C1 1C1 2C1 3C1 4C1 5C2 1C2 2C2 3C2 4C2 5C1 5C2 6C2 7C2 8C2 9C1 6C1 7
即使在通過 SOAP 調用時,CAO 啟用也會保留狀態,並且允許通過 SOAP 來回傳遞對象引用。名稱和值都保留在伺服器上的類執行個體中,並且引用可以正確工作。這兩種指令碼都調用相同的編譯 C# 組件,只是 .NET Remoting 啟用模型不同。
除了使用
CreateObject 調用 CAO 啟用外,還可以使用帶有 COM+ 的Moniker,它可以提供 CAO 啟用來替代 WKO(類型名稱和程式集Moniker)。以下指令碼:
'直接建立兩個對象set c1=GetObject("soap:typename=RefPass.Child,assembly=RefPass")set c2=GetObject("soap:typename=RefPass.Child,assembly=RefPass")'設定第一個對象的名稱,並調用數次'以遞增對象內部計數器c1.SetName("C1")WScript.Echo c1.CountUp()WScript.Echo c1.CountUp()WScript.Echo c1.Countup()WScript.Echo c1.CountUp()WScript.Echo c1.Countup()'設定第二個對象的名稱,並調用數次'以遞增對象內部計數器c2.SetName("C2")WScript.Echo c2.CountUp()WScript.Echo c2.CountUp()WScript.Echo c2.Countup()WScript.Echo c2.CountUp()WScript.Echo c2.Countup()'建立父物件set p=GetObject("soap:typename=RefPass.Parent,assembly=RefPass")'將子物件傳遞到父物件,並從父物件調用子物件WScript.Echo p.SetRef(c1)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)WScript.Echo p.CountUp(c2)'現在調用儲存在父物件內部的子物件dim c9WScript.Echo p.CountUp(c9)'從父物件擷取該對象並直接調用Set c3 = p.GetRef()WScript.Echo c3.CountUp()
將顯示以下輸出內容:
C:\moniker>refpasscaMicrosoft (R) Windows Script Host Version 5.6Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.C1 1C1 2C1 3C1 4C1 5C2 1C2 2C2 3C2 4C2 5C1 5C2 6C2 7C2 8C2 9C1 6C1 7
這與上面的 VBScript
CreateObject(
ProgID
) 樣本的輸出內容相同。因為常規 COM+ 啟用路徑被 SOAP 代理應用程式截獲,所以可以使用
CoCreateInstance、
CreateInstance 以及其他傳統的 COM+ 啟用方法來調用使用 COM+ Web 服務的用戶端啟用的對象。
程式集和類型名稱Moniker,對於從Managed 程式碼用戶端遠程擷取預先配置的用戶端啟用也很有用,如下例所示:
Imports SystemImports System.Runtime.InteropServicesModule RefPassClSub Main() Dim ChildMoniker = "soap:assembly=RefPass,typename=RefPass.Child" Dim ParentMoniker = "soap:assembly=RefPass,typename=RefPass.Parent" Dim c1,c2,p as Object c1 = Marshal.BindToMoniker(ChildMoniker) Console.WriteLine(c1.SetName("C1")) Console.WriteLine(c1.CountUp()) Console.WriteLine(c1.CountUp()) Console.WriteLine(c1.CountUp()) Console.WriteLine(c1.CountUp()) Console.WriteLine(c1.CountUp()) c2 = Marshal.BindToMoniker(ChildMoniker) Console.WriteLine(c2.SetName("c2")) Console.WriteLine(c2.CountUp()) Console.WriteLine(c2.CountUp()) Console.WriteLine(c2.CountUp()) Console.WriteLine(c2.CountUp()) Console.WriteLine(c2.CountUp()) p = Marshal.BindToMoniker(ParentMoniker) Console.WriteLine(p.SetRef(c1)) Console.WriteLine(p.CountUp(c2)) Console.WriteLine(p.CountUp(c2)) Console.WriteLine(p.CountUp(c2)) Console.WriteLine(p.CountUp(c2)) Dim c9 Console.WriteLine(p.CountUp(c9)) Dim c3 = p.GetRef() Console.WriteLine(c3.CountUp()) End SubEnd Module
編譯並運行此 Visual Basic .NET 應用程式,將產生與前面兩個 VBScript CAO 樣本相同的輸出內容。
因為伺服器應用程式將組件發布為 CAO 和 WKO 兩種形式,所以由遠程用戶端選擇啟用方法。雖然可能只對學術研究有意義,但是單一用戶端電腦確實可以使用同一組件的兩種遠程啟用方法,訪問遠程伺服器上同一個 SOAP 發布的虛擬根。