上下文件(Context

    任何程序都需要在某个平台提供的环境中执行。对于传统的操作系统。这种运行时环境的主要形式就是一个进程。一个进程不仅为你的代码提供了虚拟内存、线程和内核对象这样的资源,还为你的和别人的代码间提供了一个隔离边界。这种隔离不仅带来了某种程度上的保护作用,这样你的代码不会受别人代码错误的影响,还给你的程序提供一些独有的特性,比如单独做安全配置,或者当前目录路径。

    代码隔离在进程之间和进程内部都是有用的。COMCOM+和公用语言运行库都已经建立了这种模型以在进程内部提供细颗粒的代码隔离。在COM+CLR中,这种细颗粒的隔离单元被称为上下文(Context)。

    COM+上下文是COM单元的一部分,而COM单元本身是用于对线程对象分组的进程的一部分。CLR上下文是CLR应用程序域的一部分,而CLR应用程序域本身是用于对共享相同安全和版本策略的对象分组的进程的一部分。上下文是COM+.NET企业服务编程模式和架构核心。每个被配置在COM+目录中的组件都会被自动地关联一个上下文。这种上下文使得系统拦截成为可能,从而能够提供各种服务。

    COM+里配置的那些对象总是在一个上下文内运行。对象所需要的上下文由服务组件类上设定的上下文特性所定义。这些上下文特性定义了一个组件对于运行环境的需求。如果这个对象的调用者已经运行于一个上下文中(因为它也是一个已配置的组件),则会检查这个上下文是否和这个对象所需要的相兼容。如果是,则这个对象将在调用者所在的上下文中运行。

    如果这个上下文和这个对象所定义的不兼容,则建立一个新的上下文。CLRCOM+都依赖于代理以保证对象之间的上下文边界。代理是处于不同上下文中的真实对象的一个替身对象,代理负责在一个方法调用前后转换上下文。

    既然上下文是根据一组用于声明对象所需运行环境的属性来规定的,为一个对象定义一个上下文意味着这个对象只能从这个上下文内部直接访问。如果访问该对象的调用者所在的上下文有所不同的话,则它不能直接调用该对象上的方法,而要通过一个代理。

    下图所示的是这种情景的一个示例。对象A定义了它需要一个带有Synchronization=NoSupported属性的上下文,而对象B需要的是Synchronization=Required。这样AB分别运行于两个不同的上下文中。如果对象A想要调用对象B上的方法,则不能直接这么做;它必需通过一个代理。代理对象调用中间代码。这些中间代码可以满足运行于上下文Y的对象的需求。辟如,通过锁定。

对象C以对于上下文有着和对象A一样的需求。如果对象A调用了对象C上的一种方法,则对象C可以直接访问,而不需要代理。



  .NET Remoting
上下文

基类ContextBoundObject.NET对象会被绑定到一个上下文。为了定义这些继承类的上下文属性,要使用特性(attribute),而这些特性必须直接或间接地继承自ContextAttribute类。在.NET1.1中,有文档记载的继承了ContextAttribute的类型只有一个:即SynchronizationAttribute

.NET用于实现跨上下文通信的技术是.NET Remoting。所以ContextBoundObject类和SynchronizationAttribute被放在System.Runtime.Remoting.Contexts命名空间下。不要为在同一个应用域内部的跨上下文通信担心性能问题,在这里并没有使用TCP或者HTTP信道,.NET Remoting 使用了一个特别的应用程序域信道,这样就可以保持在进程内部操作(而不需要使用进程外的套接字服务)。

什么是应用程序域:对于Win32应用程序,应用程序边界就是进程。每个进程都有自己的虚拟内存,所以多个不同的进程不能相互影响。.NET 有另外一个安全边界:应用程序域。一个单一的进程可以包含多个应用程序域。来自一个域珠对象不能和其他域的对象直接相互操作。为了让不同的应用程序域内的对象可以相互通信,需要使用.NET Remoting。在这种进程内部时行跨应用程序域互操作的情况下,用的是应用程序域信道。

应用程序域可以做为在同一个进程中的多个应用程序的安全边界,也可以用于动态加载代码,这样在用完之后可以用卸载应用程序域的方法显式地从内存移除它们。

应用程序域可以通过编程的方式创建,而有些.NET托管环境自己也会创建应用程序域。ASP.NET运行时就是一个例子。它使用多个应用程序域来隔离多个WEB应用程序。

         下面用一个例子来演示使用.NET上下文实现对对象的同步访问。在示例中类型AContextBoundObject继承,并且被标记了[Synchronization]特性,所以对它的访问都是同步的。为了演示对类型A的对象的访问是同步的,使用了两个线程来访问Method1。在Test类的Main方法中,建立了一个Test类的实例,而此实例有个类型A成员对象。然后,启动了一个线程,此线种调用方法Method1,最后主线程再交调用方法Method1通过调用t1.Join,主线程将一直等待到t1结束。这两个线程都已被命名,这样可以很容易看到调用方法的是哪个线程。

         示例:

        

然后你可以执行应用程序查看结果。Method1启动后,在它结束前,它又会被另外一个线程启动一次。线程的顺序可能和这个不一样。对这个对象的所有访问者是同步的。

Thread 1Method1 started

Thread 1:  Method1 finished

Thread 2:  Method1 started

Thread2:  Method1 finished

如果把特性[Synchronization]从类型A上去掉的话,则两个线程会并发地运行,如下所示:

Thread 1Method1 started

Thread 2:  Method1 started

Thread 1:  Method1 finished

Thread2:  Method1 finished

 

服务组件的上下文

         所有服务组件的基类都是System.EnterpriseService命名空间下的ServicedComponent.这个类弄继承于System.ContextBoundObject,所以所有服务组件都有一个.NET Remoting上下文.和前面的例子不一样的是,对于服务组件,这里有更多的特性可以用来设置上下文.

         考虑这样一个使用上下文的例子。如图2-2所示的对象A定义了特性Transaction = RequiredSynchronization = Required。这样在对象A创建时,由它的[Transaction]特性指示的,会有一个事务自动地被创建。在图中,此事务的ID0815。除了事务,根据[Synchronization] 特性,这个对象还有同步的需要。为了同步地访问对象,COM+使用了活动(activities)。这个新创建的活动的ID4711。对象A调用了对象B上的一种方法。而对象B的特性是[Transaction = Required][Sychronization = RequiredNew]由于同步上的设置不同,B需要一个新的上下文,这样将会使用一个代理来拦截AB的调用。由于两者对事务的需求一致,这个代理只是把事务直接传递给对象B。然而,同步服务就不一样了,对象B需要一个新的活动,新创建的活动的ID5123

        

2-2

         这个例子显示了对上下文需求的不同将导致CLR运行时使用一个代理来进行方法调用的拦截。这个拦截在对象创建时和方法调用时发生。

         对象激活拦截:当一个被配置的组件实例化时,这具操作会被COM+运行时拦截。拦截使得对象池成为可能,这样就不总是需要创建一个新对象,而可以直接从对象中取出一个对象来使用。通过拦截,也能通过组件的配置,把一个初始化字符串传给一个新创建的对象。

         方法调用拦截:当一种方法被调用时,如果调用者和被调用者所在的上下文不同,方法调用拦截就会发生。例子有事务,既时激活和对终止(方法调用时对象激活,结束后对象终止)。

         COM+1.5中,调用拦截是由本机代码实现的。而在另外一个方面,.NET组件用的是托管代码。可能有人会认为COM互操作会带来性能问题。事实上,大部分情况下这不会成为问题,因为当一个.NET组件调用在同一个企业服务应用程序中另外一个.NET组件时,并没有用到COM互操作。如图2-3所示,如果调用者和被调用都是用托管代码编写的,那么方法调用是在托管代码中直接进行的。当然,服务功能是在非托管代码中实现的。在这里托管代码和非托管代码之间确实有转换,但是这和方法参数无关。这就是不需要把数据marshal到非托管代码,也不需要unmarshal到托管代码的原因。

        

2-3方法调用拦截

         访问上下文信息:你可以使用只有静态属性和方法的ContextUtil类访问上下文信息。这个类的静态属性您可以参考MSDN

混合.NETCOM+上下文

    运行在COM+应用程序的COM对象也带有上下文。我们已经在前面讨论过运行在一个.NET企业服务应用程序中的.NET组件带有.NET上下文。那么在同一个应用程序内,一个.NET组件使用一个COM组件的情况又会如何?如果这两个对象都要参与同一个事务呢?这里有个好消息,这是可以做到的,.NETCOM互操作能力在这里扮演了重要的角色。

    COM对象对.NET上下文一无所知;它们只能在COM+上下文。为了使互操作成为可能,你必须注意.NET组件的以下行为:当一个.NET服务组件被创建时,此时不仅建立了.NET上下文,还建立了COM+上下文。这是通过一人未记载的,跟ServicedComponent类有关的特性[ServicedComponentProxy]类来实现的。这个特性类型ServicedComponentProxyAttribute继承自ProxyAttribute类,而通过[Proxy]特性,你可以设置一个自定义的代理类来代替默认的代理类。.NET企业服务利用了这个功能,ServicedComponentProxy类被用做调用.NET组件代理。这个代理使得.NET上下文和COM+相连。

    下面的两节中讲述了一个.NET组件调用一个COM对象,以及一个COM对象反过来调用一人.NET组件的情况。

    .NET组件调用COM对象:当一个.NET服务组件被创建时地,此时不仅建立了.NET上下文,还建立了一个连接到.NET上下文的COM+上下文。如果此时该.NET组件创建了一个配置在COM+对象的话,如果该COM+对象对环境的需求和此.NET组件相同,则这个COM对象将在同一个上下文上运行。

    .NET组件通过一个运行时调用包装类(Runtime Callable Wrapper/RCW)来使用COM对象,这样这个COM对象看起来和一个.NET对象一样(见图2-4)。


2-4 .NET调用COM对象

         COM对象调用.NET组件:2-5反映了相反的情形,一个COM对象调用一个.NET组件上的方法。COM.NET上下文一无所知,所以当COM对象实例化后,只创建了COM+上下文。如果这个COM对象创建一个.NET对象,则一个.NET上下文会被创建。这个.NET上下文将会和COM+上下文想关联,这样两者就可以共享事务。


2-5 COM调用.NET组件

         为了使.NET对象上调用者看起来像一个COM对象,这里用到了COM调用包装类(COM Callable Wrapper/CCW)。

posted on 2007-10-04 15:33  nacarat  阅读(649)  评论(0)    收藏  举报