2000年7月1日, 微软正式宣布 Microsoft® 。NET成为 网络为中心的新一代开发平台。.NET的核心就是公共语言运行时 (CLR), 新一代组件技术继承自(且向前兼容) COM。 公共语言运行时在很多方面改进了COM, 最重要的就是使用元数据描述组件实现和其他组件依赖的所有类型。所有的类型信息都可以通过反射获得和属性扩展。 这样就可以轻而易举创建任何开发者梦寐以求的复杂的组件, 如自动支持对象的序列化。 CLR公共语言运行时是COM的取代者, 但是却不是 COM+的取代者。 COM+是为对象提供一组可以方便建立分布式系统的服务的运行时环境。 当你使用COM时如果你发现COM+有用,那么当你使用公共语言运行时的时候你同样会看到COM+非常有用。 本文是关于如何使用CLR进行COM+编程的。我们先快速回顾一下COM+, 然后就是.NET Enterprise Services。 快速回顾COM+ 所有的COM+服务都是在上下文环境里实现的。 上下文环境是一个提供运行时服务或者对象驻留场所的进程类的空间 。当一个上下文环境里的对象(或线程) 调用另外一个上下文环境里的对象, 方法就会被截取, 如图1所示。 代理给COM+ 运行时预处理或迟处理请求以及执行满足对象需求的服务代码的机会, 例如调用序列化, 声明事物管理等。 图1 上下文环境和侦听 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+ 服务象队列组件)。 这段代码如何使用一个类属性。 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+ 服务象队列组件)。 下面展示了如何使用程序集属性。 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的方法。 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)实现,看起来不和情理, 但是你将很快明白这个的重要性。 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+将侦听声明事务提交的组件。 COM+安全服务通过调用上下文环境暴露其行为,不是对象的上下文环境。System.EnterpriseServices.SecurityCallContext 类包装了CoGetCallContext, COM+ API for 获取调用上下文环境。SecurityCallContext使用一组方法和属性列表图7。调用上下文环境的功能。 这个静态属性, 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命令是: 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代码会找到这里。 实现客户端 当一个基于CLR 组件服务类被编译和部署,你就需要在客户端调用它。长远来看,组件服务类没什么特别之处;事实是使用COM+ 运行时服务是不相关的。 这个代码展示了简单类早期的定义MyTxCfgClass: using ESExample; public class Client { public static void Main(string[] args) { MyTxCfgClass tcc = new MyTxCfgClass(); ... // use object as desired } } 当然,与所有的CLR代码,客户端必须引用组件服务类程序集。 csc /r:ESExample.dll Client.cs 细微的复杂性 这里你会看到,服务类通过 .NET CLR实现COM+是相当的容易。 System.EnterpriseServices 命名空间里的类提供了可以使用COM+运行时服务的API。运行时服务本身没什么改变;他们工作的方式还是 COM+开发者熟知的。已经说了, 集成COM+和CLR 没有涉及到如此细微的复杂性。 有几个问题是使得使用CLR语言写 COM+ 代码比我刚才提到的复杂的多。 有些问题会产生,因为CLR毕竟不是 COM, 因为它可以做COM做不到的事情。这些问题是不能解决的; 你只要知道如何CLR的特性如静态方法和垃圾回收器 如何影响COM+ 编程。 再学习这些问题前,你必须知道CLR 的类和 and COM+ 上下文环境的关系。 首先,调用继承自ServicedComponent的类的实例都会被COM+ 上下文环境边界侦听。这些对象称为上下文环境边界。调用没继承自ServicedComponent的类的实例就不会被上下文环境边界侦听。 这些对象称为context-agile。CLR对象默认就是context-agile。 当你继承自ServicedComponent它们会变成context-bound 。(这个与 .NET remoting 上下文环境没有关系,我下面会讲到。) 图 9说明的这个架构 ![](https://i-blog.csdnimg.cn/blog_migrate/17e5ceef3cfb62ab9819c3b4ceb3d116.gif) 图 9 上下文环境对象 非常有趣的是CLR 对象可以实现类似COM 对象和COM+ 上下文环境交互这样的行为。通过 COM,调用对象通常默认都会被侦听; 所有的对象都是context-bound。COM 对象是context-agile 只有当它聚集了freethreaded marshaler (FTM)且不是第一个在COM+ 上下文环境里 被创建的对象(因为它不是可识别的对象), 这样情况下,调用不会被侦听。 新方法确保调用真的需要的时候被预处理和迟处理的好处是减少了侦听负荷。 特别地,如果组件服务类的实例返回了一个非组件服务类的实例 (例如一个 ADO.NET DataSet 对象),这个调用不会被侦听。 并且DataSet对象不需要做任何事情,它就是照常工作。 第二个你应该知道的事情是, 除了减少真正需要的交叉上下文侦听外, CLR/COM+集成尽可能地避免了托管类型和自有类型间的转换。 来自托管的CLR 代码到COM的调用是比较昂贵的。重要的部分就是类型的前后转换—大部分在CLR strings和COM BSTRs之间。 请求穿过COM+环境边界需要 调用一些固有的代码, 组件已经相当聪明可以避免同样来自CLR且处在同一进程里的调用者和被调用者之间的类型转换。 或许有一天所有的COM+ 运行时服务都会用CLR语言重新实现,调转就不成问题了。 那时不管如何,这个优化都会使得COM+ 侦听更加快速。 静态方法和属性 现在你知道了CLR代码如何关联上下文环境, 思考这个问题:如果一个基于CLR 组件服务类 包括静态方法和属性访问器, 它将在什么上下文里执行呢?答案是调用者。 这个看起来不是很直观, 但是它确实很有意义当你想静态成员不是对象定义且不许要在对象驻留的上下文里访问。例如,组件服务类在图 10有个静态方法,2, 和静态属性,4。这个代码会在调用者的上下文里执行。 调用实例的方法1, 或者属性3,通常在对象的上下文里执行。 这时你或许想知道当你在属性里保存了一个对象的静态引用且你想在另外一个上下问里引用会发生什么事情。基于COM的COM+编程里, 在被成为静态属性的全局变量里保存了一个原始的对象引用, 会导致严重的错误因为你不可以把对象不加包装地从原来的上下文里带走。使用基于CLR COM+ 代码, 这个就不是一个问题。 CLR 使用相当瘦的代理包装了每个组件服务类的实例子这样其它上下问的对象就不需要保留对象的直接引用。如果代码在一个保留基于CLR 组件服务类引用在静态属性的的上下文执行。它实际保留的是一个引用。如果另一个上下文里的代码要通过静态属性访问它, 特定的代理就发现变化而且封装它自己保留的引用这样侦听就触发了。这个是有个优美的解决方案,本质上你可以任何地方保存基于CLR服务类的实例并且都会正确触发。
管理珍贵的资源 我现在开始另外一个管理对象引用的话题',另外一个问题你必须注意CLR靠垃圾收集器回收内存。 这是个重要的事情因为它要自动帮助避免内存泄露并且释放闲置的内存。非常不幸的是, 垃圾收集器使得内存管理变的容易,但是他同样带来了其他问题,比如管理COM+设计的对象变的困难。因此高效地管理资源是建立可伸缩的系统的关键,这正是COM+ 的设计目标,你需要知道如果合理地释放基于CLR的资源。 考虑 CLR类使用到了SqlConnection 对象连接数据库, 如下所示: public void UseADatabase(void) { System.Data.SqlClient.SqlConnection = new System.Data.SqlClient.SqlConnection(…); …// use connection to execute statements } 在UseADatabase方法的结束部分, SqlConnection 对象被释放。 SqlConnection对象封装了处理数据库的连接并且为了效率考虑被ADO.NET底层机制池化(Beta 1 使用COM+ 对象池; Beta 2, 使用了类似COM+ 对象池的机制)。 问题是 SqlConnection对象管理的连接何时归还到对象池? 不等垃圾收集器进入,就意识到SqlConnection 对象不在被使用,就调用终结器。finalizer 把连接归还给对象池, 然后SqlConnection对象的内存就被回收。 问题是数据库连接资源是非常珍贵的资源,你不能让他们无约束地飘荡在内存里,直到垃圾收集器来回收。 需要一些方法来回收这样的资源。一种方式就是强制垃圾收集器执行回收工作,如下所示: public void UseADatabase(void) { System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient。SqlConnection(…); … // use connection to execute statements conn = null; System.GC.Collect(); } 然而,这个方法相当的笨拙且有很多潜在问题。 更好的方法是调用SqlConnection 对象实现的接口的 IDisposable.Dispose方法。 IDisposable 接口在System命名空间,它规范拥有昂贵的要清理的资源的对象通过客户端定义操作释放资源代理垃圾收集器回收的行为。SqlConnection实现了Dispose,快速归还对象给对象池 (事实上和调用finalizer的作用一样)。 下面代码是新版本的UseADatabase方法,它显示的清除了SqlConnection。 public void UseADatabase(void) { System.Data.SqlClient.SqlConnection conn = new System.Data.SqlClient.SqlConnection(…); try { … // use connection to execute statements } finally { Conn.Dispose(); } } 注意到Dispose出现在finally块里确保它可以执行,不管是否抛出异常。 那这个和COM+与服务类有什么关系呢? 首先,你的服务类很可能使用数据库连接和其它昂贵的资源, 所以如果你希望系统净化,你就应该知道如果合理地释放资源。 第二是, 所有基于CLR 服务类 实现了IDisposable.Dispose方法, 因为基类,ServicedComponent。你应该知道当客户端调用Dispose时发生了什么。默认实现的行为取决于你的组件服务类 使用了什么运行时服务。如果你没使用对象池激活, Dispose就会立即调用 finalizer (如果已经被垃圾收集器回收,就不会再调用)。如果你的组件服务类 使用了对象池而不是JIT 激活, Dispose调用Deactivate去通知对象已经离开它当前的上下文。然后它就调用 CanBePooled 去询问对象是否重用或销毁。 如果CanBePooled返回true, 对象会返回到对象池里。 返回false, 对象的finalizer就会调用 (如果已经被垃圾收集器回收,就不会再调用)。 非常重要是客户端为池化的对象调用方法Dispose,如下所示。 public void UseAPooledObject(void) { MyPooledCfgClass pcc = new MyPooledCfgClass(); try { …// use pooled object to do something } finally { Pcc.Dispose(); } } 如果不调用Dispose,对象 (如描述的数据库连接), 只有等到垃圾收集器回收资源。 最后, 如果你自己实现组件服务类的Dispose方法, 你必须调用基类的实现去实现这些描述的行为。 [ Synchronization(SynchronizedOption.Required) ] public class MyCfgClass : ServicedComponent { public new void Dispose() { … // do whatever Base.Dispose(); // call base to clean up } } 你或许想知道 JIT 激活和反激活一个对象在方法的结束。 那解决了资源的管理问题了吗?答案是否。 反激活使得对象在结束调用前释放自己成员所有的资源, 同样包括想使用相同资源的的每个方法调用。你可以通过不保存任何资源的方式达到这个目的。如果你没有选择 JIT 激活 和反激活在每次方法调用的结尾, 知道客户端调用Dispose实际上是强制你的对象重激活到饭激活并且重新终结一次。通常情况下, 到CLR的转换不会改变的JIT 激活规则。 应该避免使用它除非运行时服务需要它 (比如声明的服务) 因为它不会更高效, 且 比你自己管理昂贵的资源效率低。 展望 ServicedComponent 类继承自 System。ContextBoundObject, 它又继承自System。MarshalByRefObject, 这个基类可以使对象可以通过.NET remoting机制 被访问到。在Remoting顶层 上进行CLR/COM+集成有两个好处。首先你能通过Remoting层使用SOAP访问COM+ 运行时服务的远程对象。但是你不能够使用SOAP传递事务到另外的进程 (非你所愿, 但道理如此)。 第二点,打开了新版的新的基于CLR Remoting上下文架构实现的COM+ 运行时服务的方式 这是现在的COM+所没有用到的。 将来一定会够发生。 真要这样,服务就可以与SOAP或者其他的传输无逢集成,并且上下文就可以真的可以扩展了。那时, 已经正确跨出一大步的CLR 就会使得COM+编程变的更加容易。 | | |