COM+ Web 服务:通过复选框路由到 XML Web Services(2) (微软中事务性组件示例

80酷酷网    80kuku.com

  services|web|xml|复选框|微软

事务性组件示例


简单的计算器远算不上工作量繁重的业务应用程序,因此我们现在考虑带有对象池的适于 COM+ 事务性组件的应用程序。
最容易管理和配置的组件是由 ServicedComponent 导出的托管代码组件,如以下 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# 程序有两个类:ChildParent。如果运行以下 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+ 的名字对象,它可以提供 CAO 激活来替代 WKO(类型名称和程序集名字对象)。以下脚本:
'直接创建两个对象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 代理应用程序截获,所以可以使用 CoCreateInstanceCreateInstance 以及其他传统的 COM+ 激活方法来调用使用 COM+ Web 服务的客户端激活的对象。
程序集和类型名称名字对象,对于从托管代码客户端远程获取预先配置的客户端激活也很有用,如下例所示:
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 发布的虚拟根。

分享到
  • 微信分享
  • 新浪微博
  • QQ好友
  • QQ空间
点击: