查看原帖
目标
- 学习在 Microsoft? .NET 中使用 COM+ 服务。
- 创建被服务的组件。
- 部署被服务的组件。
要彻底理解本文内容,需要满足以下条件:
- 曾经使用过 Microsoft Transaction Server (MTS) 并在 Microsoft Visual Basic? 6.0 中分发过事务。
- 曾经使用过 COM+ 服务中的基于角色的安全性。
- 曾经在 COM+ 服务中创建和使用过队列。
- 非常熟悉 .NET 类。
- 能够在 .NET 中熟练创建控制台应用程序。
您可能曾经使用 COM+ 应用程序来托管使用 Visual Basic 或 C++ 编写的组件。COM+ 提供了许多有价值的服务,例如事务处理、排队组件、实时激活、基于角色的安全性、共享属性等。使用 COM+ 托管组件的一个主要优点在于:不必编写任何代码,就可以更改组件的行为方式,例如将组件的事务支持设置为“需要”。通过在组件服务 MMC 管理单元中,在 COM+ 组件上设置单选按钮,每次创建组件时,都将在 COM+ 事务的上下文中创建相应的组件。当组件使用 COM+ 事务时,所有数据库事务都由分布式事务处理协调器 (DTC) 处理。图 1 显示了在组件服务界面中设置“需要”事务选项的一个示例。
图 1:需要事务的 COM+ 组件示例
设置组件安全性与设置事务支持一样简单。使用 COM+ 服务管理单元,您不用重新编译代码就可以决定哪个用户可以运行哪个组件,甚至哪个方法。
NET 可以使用所有 COM+ 服务
在 .NET 框架中,只要您的类是从 System.EnterpriseServices.ServicedComponent 类派生的,就可以继续使用 COM+ 提供的所有服务。任何从 ServicedComponent 类派生的类都由 COM+ 服务托管,并且可以使用所有可用的 COM+ 服务。表 1 列出了 .NET 中支持的全部 COM+ 服务以及每个服务的简短说明。
表 1:可用的 COM+ 服务
COM+ 服务 | 说明 |
---|---|
自动事务处理 | 应用声明事务处理功能 |
COM 事务处理集成器 (COMTI) | 将 CICS 和 IMS 应用程序封装到自动化对象中 |
补偿资源管理器 (CRM) | 对非事务性资源应用原子性和持久性属性 |
实时激活 | 通过方法调用激活对象,并在调用返回时停用此对象 |
松散耦合的事件 | 管理基于对象的事件 |
对象构造 | 在构造实例时向类实例传递持久字符串值 |
对象池 | 提供现成对象的池 |
排队组件 | 提供异步消息队列 |
基于角色的安全性 | 基于角色应用安全权限 |
共享属性 | 在服务器进程中的多个对象之间共享状态 |
同步(活动) | 管理并发 |
XA 互操作性 | 支持 X/Open 事务处理模型 |
在 .NET 中使用 COM+ 服务的原因
如果要编写能够参与事务、利用基于角色的安全性或者与队列交互作用的 .NET 应用程序,就会用到 .NET 中提供的 COM+ 服务。正如您将在本文中了解到的,.NET 使这些服务很容易实现。
提示:如果 .NET 代码无需与 COM+ 服务一起工作,也就是说,您只在 .NET 框架中工作,那么请不要使用 System.EnterpriseServices,否则会造成性能下降。
COM+ 组件开发概述
在 .NET 中创建与 COM+ 服务交互的组件时,需要执行以下步骤。表 2 包含对各个步骤的说明。
- 创建类库。
- 创建所有类,使它们继承 System.EnterpriseServices.ServicedComponents 类。
- 创建程序集。
- 创建强名称。
表 2:创建 .NET 组件时使用的术语定义
术语 说明 类库 包含类的 .dll 项目类型,此类项目通常没有用户界面 System.EnterpriseServices.ServicedComponents .NET 框架中的一个类,使用它才能与 COM+ 服务进行交互 程序集 对项目中所有类和接口的描述 强名称 从程序集生成 GUID,以便可以使用 COM+ 服务注册组件
开发基于事务的组件
在本文的第一部分,您将学习如何创建使用 COM+ 的事务性服务的 .NET 组件,还将学习如何编写组件以及与此组件交互的前端应用程序。
创建 COM+ 事务组件
要使 .NET 组件能够在 COM+ 服务下运行,需要执行以下步骤。首先,必须创建一个从 System.EnterpriseServices.ServicedComponent 类派生的类。这个基类提供了与 COM+ 服务交互所需的所有适当方法和属性。需要将该类标记为需要新事务,并将所创建的任何方法标记为在没有错误出现的情况下可以自动完成事务处理。下面是具体操作。
- 打开 Microsoft Visual Studio? .NET,创建一个新的 ClassLibrary 类型的项目。
- 将 Class1.vb 文件重命名为 COMPlusServices.vb。
- 打开 COMPlusServices.vb 文件,将类名 Class1 更改为 COMPlusServices。
- 在此新类中键入如下所示的代码:
Imports System.EnterpriseServices
Imports System.Reflection
'********************************************
' COM+ 注册详细信息
' 提供 COM+ 应用程序名
<Assembly: ApplicationNameAttribute("ComPlusExample")> ' 提供具有强名称的程序集
<Assembly: _
AssemblyKeyFileAttribute("bin/ComPlusExample.snk")>
'******************************************** <TransactionAttribute(TransactionOption.required)> _
Public Class COMPlusServices
Inherits ServicedComponent
Public Sub New()
MyBase.New()
End Sub
<AutoComplete()> Public Function DoTransaction() _
As String
Return "COM+ 成功"
End Function
End Class
此代码以导入多个名称空间开始,从而在声明组件时省略了一些键入内容。 - 接下来是 COM+ 注册详细信息。请输入以下代码行:
' 提供 COM+ 应用程序名
<Assembly: ApplicationNameAttribute("ComPlusExample")>
此行指定 ApplicationNameAttribute 的值为 ComPlusExample。这是在 COM+ 目录中注册该 COM+ 应用程序时要使用的名称。第一次调用此组件后,当打开 MMC 管理单元的 COM+ 应用程序文件夹时,您会发现应用程序名称就是这个名称。
接下来的代码声明 AssemblyKeyFileAttribute 属性:
<Assembly: _ AssemblyKeyFileAttribute("bin/ComPlusExample.snk")> |
此属性通知 COM+ 目录有关强名称的信息所在的位置。在后面的步骤中,将创建用于描述 COM+ 组件的 .SNK 文件。
最后,使用以下代码声明类名 COMPlusServices:
<TransactionAttribute(TransactionOption.required)> _ Public Public Class COMPlusServices |
此类名前面的属性通知 COM+,您要将事务属性设置为“需要”。添加此行代码相当于打开 COM+ 应用程序管理单元(如图 1 所示)并手动设置此属性。
该类中的下一行代码继承 System.EnterpriseServices 名称空间中的 ServicedComponent。
Inherits ServicedComponent |
如果不包含此行,将无法使此组件在 COM+ 下运行。
添加事务方法
现在,类已经设置完毕,可以创建真正能够实现某些功能的方法了。在您编写的代码中,DoTransaction 函数返回一个字符串值,但它只给出了您必须使用的语法,如果不使用此语法该方法将无法参与事务。
<AutoComplete()> Public Function DoTransaction() As String Return "COM+ 成功" End Function |
在此方法前面加上 <AutoComplete()> 属性很重要。这表示只要此方法中没有出现异常,当它结束时就会自动调用 SetComplete。如果该方法中存在异常,则 .NET 运行时将自动调用 SetAbort 方法。这与在 Visual Basic 6.0 中编写 COM 组件不同,在 Visual Basic 6.0 中,必须手动显式调用 SetComplete 和 SetAbort。
创建强名称
在编译组件之前,您需要为此组件的程序集指定一个强名称。如果不指定,COM+ 目录将不能识别该组件,也就无法注册它。实际上,您已经通过前面使用的 AssemblyKeyFile 属性指定了强名称,现在需要使用强名称工具 (Sn.exe) 创建强名称并使 GUID 与程序集关联。
- 打开命令提示。
- 要创建强名称,请在命令提示下键入以下代码,然后按 Enter 键。
sn -k ComPlusExample.snk - 将 ComPlusExample.snk 文件从硬盘驱动器的根目录(通常为 C:/)复制到项目所在文件夹的 bin 目录下。
现在,需要编译此程序,使它能生成在 COM+ 注册此组件必需的文件。在 Visual Studio .NET 中,在 Build(生成)菜单上,单击 Build(生成)。
生成客户端测试应用程序
现在,组件生成完毕,需要生成客户端应用程序来调用并测试此组件。创建简单的控制台应用程序,此程序中的模块文件的 Main 方法创建新组件实例,并调用 DoTransaction() 方法。以下是主要步骤:
- 在 Visual Basic .NET 中,创建新的控制台应用程序项目。
- 添加对刚创建的组件的引用。
- 键入如下所示的代码。
Module modMain
Sub Main()
Dim objCOMPlus As New _
COMPlusJumpStart.COMPlusServices()
Console.WriteLine(objCOMPlus.DoTransaction)
Console.ReadLine()
End Sub
End Module
试一试
最后,运行此程序并查看其运行情况。
- 打开组件服务 MMC 管理单元,验证您的组件已经动态注册到 COM+ 目录中。您应该看到图 2 所示的界面。
- 编译并运行控制台应用程序。
图 2:COM+ 目录中的新建 .NET 服务组件
基于角色的安全性
如果多个用户调用在 COM+ 下运行的 COM 组件,您需要进行验证,只允许特定用户访问某个组件。COM+ 允许您定义角色并为其分配 NT 用户。定义并设置角色后,就可以指定哪些角色可以运行哪些组件,甚至指定可以运行组件上的哪些方法。
请在此 COMPlusServices 类中添加一个方法,以添加基于角色的安全性。您需要创建名为 Managers 的角色,并在新方法中进行测试,查看调用者是否为 Managers 角色。 添加基于角色的安全性的步骤
不用直接从组件服务 MMC 管理单元修改 COM+ 应用程序来添加安全角色,只需在项目中添加一个新属性。使用 SecurityRoleAttribute 类添加新的 Managers 角色。这个类的构造函数有两个参数:role(字符串值)和 everyone(布尔值)。role 参数指定要创建的角色的名称,everyone 参数指定是否将内置 Everyone 组添加到此角色的用户中。
- 在注释“COM+ 注册详细信息”下键入以下代码,将新的安全性角色添加到 COM+ 应用程序中。
'********************************************
' COM+ 注册详细信息
' 基于角色的安全性属性
<Assembly: SecurityRoleAttribute("Managers",False)> - 更改安全级别设置,以便在进程级和组件级执行访问检查。这使 COM+ 应用程序具有安全性调用上下文。
- 显示 COM+ 服务管理单元。
- 单击 Security(安全性)选项卡并更改安全级别,如图 3 所示。
图 3:在 COM+ 目录中设置安全级别属性
除了手动进行处理外,还可以在组件中添加属性,通知组件执行访问级别检查。以下是在 COMPlusServices 类一开始的“COM+ 注册详细信息”部分添加的代码。
<Assembly: ApplicationAccessControlAttribute (AccessChecksLevel:=AccessChecksLevelOption.ApplicationComponent)> |
检查安全性角色
现在,将新方法添加到名为 IsManager 的类中。此方法将检查用户是否为具有 Managers 角色的成员。此方法是一个函数,它返回一个布尔值,表明调用者是否为 Managers 角色。要访问调用该方法的用户的安全上下文,需要使用 SecurityCallContext 类。通过调用 CurrrentCall 方法,获得当前用户的上下文。然后调用 IsCallerInRole 方法,并传递 Managers 作为此角色的名称。
1、将以下方法添加到 COMPlusServices 类中。
Public Function IsManager() As Boolean Dim objCallContext As SecurityCallContext = _ SecurityCallContext.CurrentCall IsManager = _ objCallContext.IsCallerInRole("Managers") End Function |
现在,需要重新生成该组件以测试此新方法。
2、从 Visual Studio .NET 的 Build(生成)菜单中,单击 Rebuild Solution(重新生成解决方案)。
试一试
Sub Main() Dim objCOMPlus As New _ COMPlusJumpStart.COMPlusServices() Console.WriteLine(objCOMPlus.DoTransaction) Console.WriteLine(objCOMPlus.IsManager().ToString) Console.ReadLine() End Sub |
2、在命令提示下键入您编译的可执行文件的名称,运行控制台应用程序。
第一次运行该代码时会发生异常,并说明访问被拒绝的原因是 Managers 角色中没有添加任何用户。要纠正这个异常,请将您自己添加为 Managers 中的用户,并再次运行该应用程序。这次应该不会出现任何异常。也可以在代码中添加异常处理。以下是添加了异常处理代码的客户端应用程序示例:
Sub Main() Try Dim objCOMPlus As New _ COMPlusJumpStart.COMPlusServices() Console.WriteLine(objCOMPlus.DoTransaction) Console.WriteLine(objCOMPlus.IsManager().ToString) Console.ReadLine() Catch objException As Exception Console.WriteLine("出现错误。 " _ & "详细信息: " _ & objException.Message) Console.ReadLine() End Try End Sub |
使用排队组件
在 COM+ 应用程序中,添加排队支持很简单。只需确保该应用程序作为服务器程序运行(进程外),然后在选项卡上设置 Queued(排队)和 Listen(侦听)属性。完成这些设置后,客户端应用程序就可以异步或同步调用组件。该功能的精妙之处在于,不必更改 COM 对象中的代码,只需在 COM+ 目录中更改其属性。
.NET 框架支持排队组件,而且,正如您期望的那样,您可以通过使用属性,而不用手动更改 COM+ 目录即可为组件提供排队支持。
请在 COMPlusServices 类中添加一个方法,然后在 .NET 客户端应用程序中使用 COM+ 排队组件服务异步调用它。 将 COM+ 应用程序作为服务器程序(进程外)。这是排队组件的要求。要使用属性达到此目的,请将以下代码添加到项目中:
'******************************************** ' COM+ 注册详细信息 <Assembly: ApplicationActivationAttribute(ActivationOption.Server)> |
在组件中添加排队支持。使组件可访问 MSMQ 队列,并使其侦听自己的队列以处理消息。下面是使用属性完成此过程的代码:
'******************************************** ' COM+ 注册详细信息 <Assembly: ApplicationQueuingAttribute(Enabled:=True, QueueListenerEnabled:=True)> |
在类 QueueTest 中添加一个方法。确保它是子例程且不返回任何值。使用它在 Windows 应用程序日志中写入信息。此代码应如下所示:
Public Sub QueueTest() System.Diagnostics.EventLog.WriteEntry(_ "COMPlusServces","队列测试",_ Diagnostics.EventLogEntryType.Error) End Sub |
就到这里吧。这就是为使组件成为 COM+ 排队组件需要完成的全部操作。
试一试
现在,应该通过创建另一个控制台应用程序并调用此组件,来尝试运行该排队组件。
Sub Main() Dim objTest As COMPlusJumpStart.COMPlusServices Dim strMoniker strMoniker = _ "queue:/new:COMPlusJumpStart.COMPlusServices" objTest = GetObject(strMoniker) objTest.QueueTest() End Sub |
它将异步调用组件中的 QueueTest 方法。如果要同步调用此方法,可以象调用该组件中的所有其他方法一样调用它。
现在,可以运行此控制台应用程序,尝试运行此排队组件。
自 Visual Basic 6.0 以来 COM+ 的变化
您已经通过 Visual Basic 6.0 或 COM 了解了 .NET 中的许多内容。现在,您已经获得了在 .NET 框架中工作的好处,这有助于实现对象的平稳交互,您只需编写少量代码,执行少量维护即可。
这类简化操作中最典型的是,在 Visual Basic 6.0 中,必须手动显式调用 SetComplete 和 SetAbort。而在 .NET 中,SetComplete 和 SetAbort 由 <AutoComplete()> 属性调用。
Visual Basic .NET 和 Visual Basic 6.0 的另一个不同之处是,由于使用单线程单元线程模型,Visual Basic 6.0 组件不能使用 COM+ 对象池。而 Visual Basic .NET 组件是 .NET 组件,支持任何单元线程模型,所以可以使用 COM+ 对象池。
总结 可以看出,利用 .NET 框架可以方便地利用 COM+ 提供的服务。这意味着您可以将新的 .NET 组件添加到使用 COM 和 COM+ 编写的现有程序中,而且它们可以协同工作。这一点非常重要,因为这意味着您不必放弃现有的全部 COM 和 COM+ 代码。如果您从头开始生成新应用程序,强烈建议您完全在 .NET 框架中生成它,因为这样效率会更高。