今天我尝试翻译一下这个非常不错文章,只为与大家分享学习.英文题目:COM+ Integration How _NET Enterprise Services Can Help You Build Distributed Applications,中文题目:COM+集成:.NET Enterprise Services 如何帮你建立分布式应用.作者简介:作者Tim Ewald: DevelopMento的首席科学家, 最近出版的 事务性COM+: 创建可伸缩的应用系统 (Addison-Wesley, 2001)的作者.(本文由FrankXu Lei亲自翻译\校订,如有错误希望指正,).另外在此表达对作者的敬意
COM+ Integration How _NET Enterprise Services Can Help You Build Distributed Applications
COM+提供了为需要运行时服务的类所需要的运行时服务。 如果一个类需要服务,COM+会确保实例驻留在能够提供服务的上下文环境里。例如,如果一个类使用了声明的事务,COM+确保每个实例驻留在为对象提供使用的分布式事务的上下文环境里。类是通过声明属性使用COM+运行时服务。每个服务声明用来控制自身行为的属性。 使用COM+运行时服务和标志了声明的属性的类称为配置类。没有使用COM+服务的类或者没标志任何声明的属性的类,称做非配置的类。 COM+在一个称做目录的元数据库存储配置类声明的属性值。 这个目录被分割在控制配置类的实例映射到进程的应用中。有两种应用程序, 库和服务应用程序。 每个配置类属于单个应用。如果一个配置类在库应用程序中, 这种类型的对象通常在客户端创建。如果配置 类在服务应用中,这个类型的对象通常在专有的代理进程dllhost.exe里创建。 当一个配置类被初始化, 对象的创建者必须确保新对象消亡在提供运行时服务的上下文环境里。 当一个新对象对象被创建, 对象的创建者会检查它的服务需求, 比如在类的存储在COM+ 目录里属性值。 如果对象的需求可以被创建者的上下文环境满足, 新对象就驻留在那里,调用不会被侦听。如果新对象的创建者上下文环境不能满足它需求, 新的上下文环境将创建来提供对象所需要的运行时服务。新对象 驻留在新上下文环境里,所有的调用都会被侦听以便确保对象对象访问了它所需要的服务。当配置类被初始化,对象创建者通常将新对象放在创建者的上下文环境。 图 2 对象上下文环境 和 调用上下文环境对象 一个特定上下文环境里的对象也许想要与提供给它上下文环境的运行时服务交互。COM+ 为每个上下文环境建模为一个COM 对象,称作一个对象上下文环境对象 (简称对象上下文环境)。它同样为每个执行的逻辑线程(因果关系)建模为COM 对象,称作一个调用上下文环境 对象 (简称上下文环境)。任何上下文环境里执行的代码能够通过CoGetObjectContext 和 CoGetCallContext获得对象的引用,。(等价与Visual Basic 6.0里的 GetObjectContext and GetSecurityCallContext)。图2说明了这个架构。
用
CLR
实现
COM+
服务类
和
COM
一样,
CLR
依赖与
COM+
提供方便开发者建立可伸缩的应用系统的运行时服务。使用
CLR
实现
COM+
服务类相当容易,某些时候,
比用
COM
实现更加高效。要知道单独通过
COM
的互操作难以实现两种技术的集成。因为当你使用
CLR
实现使用
COM+
的
COM
组件,
CLR
和
COM+
的集成度已经很深,
产生了一个更好的与其他的
CLR
技术如
Remoting
和代码访问安全集成的编程模型。
CLR
访问
COM+
的
API
有命名空间
System.EnterpriseServices
类定义。
CLR
依赖
COM+
的类使用这些类来设置属性和与对象交互以及调用上下文环境。
System.EnterpriseService 里最重要的类是 ServicedComponent 。 所有使用 COM+ 运行时服务的 CLR 类 必须扩展 ServicedComponent , 如下所示:
using System.EnterpriseServices;
namespace ESExample
{
public class MyCfgClass : ServicedComponent
{
public MyCfgClass() {}
…
}
}
ServicedComponent
出现在被继承的位置就是告诉
公共语言运行时的对象创建者类已经被设置,这个类可能需要使用
COM+
上下文环境。
继承自
ServicedComponent
的类必须是公开和实际的,
必须提供缺省的公共的构造函数。
|
声明的属性
一个继承自
ServicedComponent
的服务组件类是否需要上下文环境取决于他的声明的属性。记住
配置的服务组件类声明的属性存储在
COM+
目录里。
使用基于
COM
的类,
把目录和适当的信息关联的任务就是你的了。
你既可以写脚本来访问目录也可以通过组件服务浏览管理器手动配置
使用基于 CLR 的组件服务类,你可以利用 CLR 固有的支持扩展元数据和直接在类的定义里嵌入相关的声明的属性。 安装时这些信息都可以从服务组件类里萃取并被设置到 COM+ 目录里。 System.EnterpriseServices 命名空间 定义了一个集合的用来设置服务类 COM+ 运行时需要的属性的类。 最重要的属性类在这里 图 3 ( 还有一些其他的控制访问其他 COM+ 服务象队列组件 ) 。
这段代码如何使用一个类属性。
使用基于 CLR 的组件服务类,你可以利用 CLR 固有的支持扩展元数据和直接在类的定义里嵌入相关的声明的属性。 安装时这些信息都可以从服务组件类里萃取并被设置到 COM+ 目录里。 System.EnterpriseServices 命名空间 定义了一个集合的用来设置服务类 COM+ 运行时需要的属性的类。 最重要的属性类在这里 图 3 ( 还有一些其他的控制访问其他 COM+ 服务象队列组件 ) 。
这段代码如何使用一个类属性。
using System.EnterpriseServices;
namespace ESExample
{
[ Transaction(TransactionOption
。Required) ]
public class MyTxCfgClass : ServicedComponent
{
public MyTxCfgClass() {}
…
}
}
这段代码里,
Transaction
属性表明类
MyTxCfgClass
需要
COM+
托管的分布式事务。
当一个类的配置信息加到 COM+ 目录里, 这个类通常就被加到了 COM+ 应用中。使用传统的基于 COM 服务类, 没办法在代码里建立这样的关联。 有了基于 CLR 的服务类, CLR 元数据的扩展性就可以提供帮助。 System.EnterpriseServices 命名空间定义了一组 属性类可以描述程序里服务类属于的应用程序集。 图4 列出了最重要的程序集级别的属性 ( 还有一些其他的控制访问其他 COM+ 服务象队列组件 ) 。
下面展示了如何使用程序集属性。
当一个类的配置信息加到 COM+ 目录里, 这个类通常就被加到了 COM+ 应用中。使用传统的基于 COM 服务类, 没办法在代码里建立这样的关联。 有了基于 CLR 的服务类, CLR 元数据的扩展性就可以提供帮助。 System.EnterpriseServices 命名空间定义了一组 属性类可以描述程序里服务类属于的应用程序集。 图4 列出了最重要的程序集级别的属性 ( 还有一些其他的控制访问其他 COM+ 服务象队列组件 ) 。
下面展示了如何使用程序集属性。
using System.EnterpriseServices;
[assembly: ApplicationName("MyApp")]
[assembly: ApplicationActivation(ActivationOption.Library)]
namespace ESExample
{
[ Transaction(TransactionOption.Required) ]
public class MyTxCfgClass : ServicedComponent
{
public MyTxCfgClass() {}
…
}
}
这个例子里,
MyTxCfgClass
被编译进标志为库应用的程序集
MyApp
里。
如果构造函数改变了
ApplicationActivation
属性为
ActivationOption.Server
,
类将会被实现到服务端程序集里。
注意到这些应用属性并没提供完全的针对所有应用的
COM+
设置。
例如,没办法用应用属性
定义服务应用应该运行那种安全规则。
这样也非常的明智,因为在
CLR
程序集元数据里里保存帐户和密码不是一个好主意。
实现接口 IObjectConstruct 和 IObjectControl
一些
COM+
运行时服务,
包括对象创建,对象池,和
just-in-time (JIT)
激活,
需要在使用他们的对象上调用
标准的方法。
特别是,
对象创建服务调用接口
IObjectConstruct
定义的构造函数去传递构造函数字符串给每个新实例。
对象池和
JIT
激活服务调用
Activate
,
Deactivate
,和
CanBePooled IObjectControl
定义的方法去通知一个对象生命周期内的事件。
如果你认为使用这些服务你在你的继承自
ServicedComponent
的服务类必须显示实现接口
IObjectConstruct
和
IObjectControl
,事实并非如此。类
ServicedComponent
已经提供了对方法实现,一个类可以重写即可。
例如,下面代码展示了一个池化的对象 可以有选择地实现接口 IObjectControl 的方法。
例如,下面代码展示了一个池化的对象 可以有选择地实现接口 IObjectControl 的方法。
namespace ESExample
{
[ ObjectPooling(true, 5, 20) ]
public class MyPooledCfgClass : ServicedComponent
{
public MyPooledCfgClass() {}
// override CanBePooled so that instances can be
// reused, use default implementations of Activate
// and Deactivate provided by ServicedComponent
public override bool CanBePooled() { return true; }
}
}
在这个例子中,
类
MyPooledCfgClass
设置了
ObjectPooling
属性,
最小数目
5
和最大数目
20
。
类重写了方法
CanBePooled
和返回
true
的实现,这样
类的实例就可以被重用。
缺省情况下返回
false
,
意味着类的实例不可以重用。类没重写
Activate
或者
Deactivate;
它依赖与
ServicedComponent
实现。
缺省的
Activate
返回
successfully
,
因此允许激活继续。
缺省的
Deactivate
什么都没做。
另外要重点指出的是类 ServicedComponent 提供接口 IObjectConstruct 和 IObjectControl 的方法缺省的实现, 实际并没。 取而代调用你的类的内部类 (ProxyTearoff) 实现,看起来不和情理, 但是你将很快明白这个的重要性。
另外要重点指出的是类 ServicedComponent 提供接口 IObjectConstruct 和 IObjectControl 的方法缺省的实现, 实际并没。 取而代调用你的类的内部类 (ProxyTearoff) 实现,看起来不和情理, 但是你将很快明白这个的重要性。
ContextUtil 和 SecurityCallContext
服务类经常需要他们使用的运行时服务交互。
COM+
通过截取实现了自己的服务,
对象通过上下文和服务交互。
System.EnterpriseServices.ContextUtil
类包装了
CoGetObjectContext
,
COM+ API
获取对象的上下文环境。
ContextUtil
通过一组静态方法暴露
COM+
对象的上下文环境
对象和属性列表在
图 5
。
在 图 6 的代码展示 ContextUtil 类是怎么用来操作 COM+ 声明的事务 happy 和 done 。 特别地, 它设置 ContextUtil.DeactivateOnReturn 属性为 true , 把 done 位打开表明声明的事务将会在方法结束调用以前完。 然后设置 ContextUtil.MyTransactionVote 属性为 TransactionVote.Abort , 关闭 happy 位表明声明的事务会终止如果一个方法没执行成功 — 换句话说, 如果抛出一个异常。然后当处理了一些数据库后,方法会设置 ContextUtil.MyTransactionVote 属性为 TransactionVote.Commit , 打开 happy 位表明 COM+ 将侦听声明事务提交的组件。
在 图 6 的代码展示 ContextUtil 类是怎么用来操作 COM+ 声明的事务 happy 和 done 。 特别地, 它设置 ContextUtil.DeactivateOnReturn 属性为 true , 把 done 位打开表明声明的事务将会在方法结束调用以前完。 然后设置 ContextUtil.MyTransactionVote 属性为 TransactionVote.Abort , 关闭 happy 位表明声明的事务会终止如果一个方法没执行成功 — 换句话说, 如果抛出一个异常。然后当处理了一些数据库后,方法会设置 ContextUtil.MyTransactionVote 属性为 TransactionVote.Commit , 打开 happy 位表明 COM+ 将侦听声明事务提交的组件。
COM+
安全服务通过调用上下文环境暴露其行为,不是对象的上下文环境。
System.EnterpriseServices.SecurityCallContext
类包装了
CoGetCallContext
,
COM+ API for
获取调用上下文环境。
SecurityCallContext
使用一组方法和属性列表
图7
。调用上下文环境的功能。
这个静态属性, CurrentCall ,为当前的调用返回一个 SecurityCallContext 的 引用。 图 8 提供了 GetCallerName 方法使用 SecurityCallContext 获取当前调用者的例子。 ( 注意到 ApplicationAccessControl 属性是在库应用 MyApp 里用来做基于角色的安全 )
这个静态属性, CurrentCall ,为当前的调用返回一个 SecurityCallContext 的 引用。 图 8 提供了 GetCallerName 方法使用 SecurityCallContext 获取当前调用者的例子。 ( 注意到 ApplicationAccessControl 属性是在库应用 MyApp 里用来做基于角色的安全 )
编译组件服务类
一旦组件服务类实现完毕,
就必须编译。编译代码相当容易,
但是要记住两样东西,首先
COM+
集成组件需要编译为强名程序集。
创建一个强名强名程序集,
你必须使用强名工具
sn.exe
创建一个密钥。然后你就可以引用这个存储在文件里的密钥。你的组件使用一个来自于命名空间
System.Reflection
称作
AssemblyKeyFileAttribute
程序集级别的属性:
using System.EnterpriseServices;
using System.Reflection;
[assembly: ApplicationName("MyApp")]
[assembly: ApplicationActivation(ActivationOption。Library)]
// AssemblyKeyFile attribute references keyfile generated
// by sn.exe - assembly will have strong name
[assembly: AssemblyKeyFile("keyfile")]
namespace ESExample
{
•••
}
第
2
,
当你编译
强名程序集,
你必须引用
System.EnterpriseServices
命名空间,
System
。
EnterpriseServices.dll
。
这里是创建密钥和编译组件服务类的命令
:
sn -k keyfile
csc /out:ESExample.dll /t:library
/r:System.EnterpriseServices.dll MyCfgClass.cs
部署组件服务类
当基于基于
CLR
组件服务类编译完成,程序集就需要部署
你可以运行服务注册工具
regsvcs.exe
。在命令行里
如下所示:
regsvcs ESExample.dll
这个工具做了
3
个事情。
第一,注册
CLR
程序集为
COM
组件
(
如果你运行了程序集注册工具
regasm.exe)
。
第二,
产生一个
COM
类型库文件
(
如果你运行了程序集类型转换工具,
tlbexp.exe)
使用它部署你的程序集在
COM+
目录实现的服务类。
Regsvcs.exe
缺省根据
ApplicationName
和
ApplicationActivation
属性创建目标你的程序集应用。
(
你可以重写这些方法使用
command-line switches
。
)
第三,
为服务类它使用
.NET
反射
APIs
询问你程序集里实现的元数据且使用这些信息更新
COM+
目录,所以每个类必须有适当的属性设置
如果你的组件服务类没有特别声明的属性,
regsvcs.exe
会使用缺省值。
通常来说,
缺省选项会关闭运行时服务。
这样减少了过载
确保你的服务类只加载组件显示声明需要的服务。有些环境,
无论无何,
当
regsvcs.exe
根据属性设置获取值时。
例如,
MyTxCfgClass
查看
图 6
既没标注
Synchronization
或
JustInTimeActivation
属性。
但是因为
COM+
声明的事物服务依赖与这俩个服务,
他们将可以使用当注册到
COM+
目录时。如果
MyTxCfgClass
标记了额外的属性,他们的值必须和事务定义的值是兼容的,
如下所示。
// all these attributes
// are compatible
[ Transaction(TransactionOption.Required) ]
[ Synchronization(SynchronizationOption.Required) ]
[ JustInTimeActivation(true) ]
public class MyTxCfgClass : ServicedComponent
{
public MyTxCfgClass() {}
•••
}
所以,这些声明的属性是使得继承自
ServicedComponent
的组件类可以用正确的信息简单的初始化
COM+
目录,
或者有更深远的意义呢?证明了,
托管的
COM+ API
服务反射不依赖
COM+
目录,
依赖你类里声明的属性去查找你要的服务。使用这些信息来约束行为。
例如, 我早先提到的 ProxyTearoff 类 ,如果你的类属性标记为 System.EnterpriseServices 里 ObjectPooling 或 JustInTimeActivation , ProxyTearoff 仅仅向前调用你的服务组件类里实现的接口否则你的对象不会被通知, 不管 COM+ 目录怎么说。
某种意义上说, COM+ 目录是你组件类的声明的属性的一个视图, 不是真正的代码 COM+ 目录里唯一的一个会变化的属性就是部署相关的,比如对象构造字符串和安全设置。 这是一个次数签名。 COM+ 目录存在因为 COM 没有可扩展的元数据模型从二进制组件里。 CLR 有, 所以你可以看到一个将来版本的完全依赖 CLR 元数据和抛弃目录的 COM+ 运行时服务。
不值得这样, 缺省情况下你类里公共的方法不会出现在 COM+ 目录里。因为他们不会出现在 regsvcs.exe 为注册生成的类型库里,这个是默认的行为当一个 CLR 类是来自 COM 。 你可以在你自己的组件服务类里重写方法或者给你的类设置 System.ClassInterfaceAttribute 属性。
除了部署一个实现了基于 CLR 的 COM+ 里的服务类的程序集,你必须部署它,基于 CLR 客户端才能够加载。你可以通过直接拷贝到客户端的目录下 ( 或者任何客户端配置的可以找到的目录里 ) 部署你的程序集为私有的,这样就只有单个客户端可以使用。或者你可以部署为公开的程序集对机器上的所有程序公开,方式是注册到全局共享程序集缓存里 (GAC) 。 你可以在命令行使用 GAC 工具 gacutil.exe ,可以使用开关 -i 命令是:
例如, 我早先提到的 ProxyTearoff 类 ,如果你的类属性标记为 System.EnterpriseServices 里 ObjectPooling 或 JustInTimeActivation , ProxyTearoff 仅仅向前调用你的服务组件类里实现的接口否则你的对象不会被通知, 不管 COM+ 目录怎么说。
某种意义上说, COM+ 目录是你组件类的声明的属性的一个视图, 不是真正的代码 COM+ 目录里唯一的一个会变化的属性就是部署相关的,比如对象构造字符串和安全设置。 这是一个次数签名。 COM+ 目录存在因为 COM 没有可扩展的元数据模型从二进制组件里。 CLR 有, 所以你可以看到一个将来版本的完全依赖 CLR 元数据和抛弃目录的 COM+ 运行时服务。
不值得这样, 缺省情况下你类里公共的方法不会出现在 COM+ 目录里。因为他们不会出现在 regsvcs.exe 为注册生成的类型库里,这个是默认的行为当一个 CLR 类是来自 COM 。 你可以在你自己的组件服务类里重写方法或者给你的类设置 System.ClassInterfaceAttribute 属性。
除了部署一个实现了基于 CLR 的 COM+ 里的服务类的程序集,你必须部署它,基于 CLR 客户端才能够加载。你可以通过直接拷贝到客户端的目录下 ( 或者任何客户端配置的可以找到的目录里 ) 部署你的程序集为私有的,这样就只有单个客户端可以使用。或者你可以部署为公开的程序集对机器上的所有程序公开,方式是注册到全局共享程序集缓存里 (GAC) 。 你可以在命令行使用 GAC 工具 gacutil.exe ,可以使用开关 -i 命令是:
gacutil -i ESExample.dll
如果基于
CLR
服务类
被部署到
COM+
服务程序,
COM+
组件需要不依赖客户端程序运行的目录加载程序集。这样的情况下,你的程序集必须部署到
GAC;
否则,
任何实例化组件服务类的操作都将失败。
.NET Framework 一个目标就是通过 XCOPY 部署简化安装。 XCOPY 借助于 MS-DOS 命令行工具从一个地方到另外的地方拷贝文件和目录。 XCOPY 部署可以不需要执行代码在远程服务器上安装部署组件。为了支持 XCOPY 部署, CLR/COM+ 集成组件允许你在安装时间执行 regsvcs.exe 和管理已经注册的基于 CLR 组件服务类。
你要知道 XCOPY 部署基于 CLR 服务类也有缺点。首先, COM+ 目录只能被 COM+ 系统应用程序定义的具有管理员权限的用户更新。 默认的仅仅是本地机器上的管理员。为了成功注册你的组件,基于 CLR 服务类的代码必须具有管理员权限。不然, 注册会失败。 ( 当然任何执行 regsvcs.exe 工具的人都具备这个权限。 )
第 2 个缺点是 如果当你简单的拷贝程序集到合适的目录,你的组件服务类安装在 COM+ 服务应用程序里还有额外的过要做。特别是设置 COM+ 服务程序的安全策略, 这个就是我刚提到的为什么不能使用元数据来实现的原因。如果你部署服务类在 COM+ 库服务里, 这个就不存在问题只要你的客户端可以找到程序集。例如,假如你在利用 CLR 实现一个 ASP.NET 程序使用的组件服务类, 你可以使用 XCOPY 安装 COM+ 库应用和程序集的类到 ASP.NET application's"bin 子目录, ASP.NET code 代码会找到这里。
.NET Framework 一个目标就是通过 XCOPY 部署简化安装。 XCOPY 借助于 MS-DOS 命令行工具从一个地方到另外的地方拷贝文件和目录。 XCOPY 部署可以不需要执行代码在远程服务器上安装部署组件。为了支持 XCOPY 部署, CLR/COM+ 集成组件允许你在安装时间执行 regsvcs.exe 和管理已经注册的基于 CLR 组件服务类。
你要知道 XCOPY 部署基于 CLR 服务类也有缺点。首先, COM+ 目录只能被 COM+ 系统应用程序定义的具有管理员权限的用户更新。 默认的仅仅是本地机器上的管理员。为了成功注册你的组件,基于 CLR 服务类的代码必须具有管理员权限。不然, 注册会失败。 ( 当然任何执行 regsvcs.exe 工具的人都具备这个权限。 )
第 2 个缺点是 如果当你简单的拷贝程序集到合适的目录,你的组件服务类安装在 COM+ 服务应用程序里还有额外的过要做。特别是设置 COM+ 服务程序的安全策略, 这个就是我刚提到的为什么不能使用元数据来实现的原因。如果你部署服务类在 COM+ 库服务里, 这个就不存在问题只要你的客户端可以找到程序集。例如,假如你在利用 CLR 实现一个 ASP.NET 程序使用的组件服务类, 你可以使用 XCOPY 安装 COM+ 库应用和程序集的类到 ASP.NET application's"bin 子目录, ASP.NET code 代码会找到这里。
本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/321003,如需转载请自行联系原作者