在一个上下文被创建时,CLR允许将上下文属性放置到合适的位置。当一个对象的代理被创建时,CLR允许上下文属性在代理和这个上下文绑定对象之间插入消息接收器.这表明了上下文特性的主要动机是充当消息接收器的工厂。
由上下文属性插入的消息接收器负责实现它们的属性(和它的特件)所表示的方面。例如,我们的线程优先级属性在进入某个上下文之前,需要插入一个消息接收器,用于调整线程优先级,而在离开上下文之前复位,消息接收器通常操纵线程的状态,上下文的状态,以及程序整个执行状态的其他部分。
上下文绑定对象的代理支持在几个众所周知的区域插入消息接收器。尤其是上下文架构将消息接收器链分成四个明显不同的区域。如图7.11所示,这些区域由系统提供的终结器接收器(terminator sink)作为分界,从而划分接收器链。不妨同顾一下,代理将适当地调整执行状态,用于切换到目标对象的上下文。在通常情形下,由上下文属性插入的消息接收器也将调整执行状态的这些方面。在消息接收器的四种不同区域中,CLR将在切换到上下文之前运行信使接收器(envoy sink),和客户端上下文接收器(client context sink)。这意味着当信使接收器调用Thread.CurrentContext时,它将返回调用方的上下文,而不是目标对象的上下文,相比之下,对象上下文接收器和服务器端上下文接收器在切换上下文后才运行,也就是说这两者之一调用Thraad.CurrantContext,它将得到目标对象的上下文,而不是调用方的上下文。
为了正确地理解消息接收器的四个区域,有必要考察谁需要插入这些接收器。对于下面的讨论,我们只考虑一种情形,就是从某个上下文上调用代理并不等于从目标对象的上下文上调用代理。在这种情形下,需要考虑两个上下文:调用方的上下文和目标对象的上下文。
目标上下文的属性很可能需要在进入上下文之前调整执行状态,这是显而易见的。这个调整工作便是服务器端上下文接收器的任务。对于实现了IContributeServerContextSink
接口的目标上下文中的每个上下文属性,CLR使它有机会在这个区域插入消息接收器。对象接收器(object sink)与服务器端上下文接收器很相似。像服务器端上下文接收器一样,对象接收器是在上下文切换到日标上下文之后才执行的。不像服务器端上下文接收器那样,无论调用指定哪个对象它都执行,对象接收器与目标上下文的特定对象是相关的。对象接收器对于实现与对象相关的特征是很有用的,例如,MTS[微软事务服务器(Microsoft Transaction Server)]风格的实时(just-in-time)激活。对象接收器由实现IContributeObjectSink接口的上下文属性提供。
服务器端上下文接收器(server context sink)截获从目标上下文而来的调用。那果上下文属性希望调整由当前上下文形成的调用的执行状态,那么,这个上下文属性就必须提供一个客户端上下文接收器(cllent context sink)。客户端上下文接收器由实现了IContributeClientContextSink接口的上下文属性提供。客户端上下文接收器通常取消或者挂起由服务器上下文接收器所做的工作。使用客户端和服务器端上下文接收器的典型例子就是实现调用定时(call timing)。假设一个服务器端上下文接收器被它自己使用,你可以很容易地记录在进入上下文和离开上下文的间隔时间:
public class ServerTimingSink:ImessageSink{
[ContextStatic]
static internal long totalTicks=0;
public Imessage SinkProcessMessage(Imessage r){
long start=DateTime.Now.Ticks;
Imessage p=next.SyncProcessMessage(r);
long end=DateTime.Now.Ticks;
totalTicks += end-start;
return p;
}
//为了清晰起见,余下部分省略
}
然而,对于这个上下文耗费的时间,与目标对象可能调用任何子上下文(child context)的时间,这个接收器将认为是一样的。为了避免考虑目标上下文之外调用所花费的时间,你也可以插入一个客户端上下文接收器,它只关注离开当前上下文的情形,并相应地调整累计时间:
public class ClientTimingSink:ImessageSink{
public Imessage SinkProcessMessage(Imessage r){
long start=DateTime.Now.Ticks;
Imessage p=next.SyncProcessMessage(r);
long end=DateTime.Now.Ticks;
ServerTimingSink.totalTicks-=end-start;
return p;
}
//为了清晰起见,余下部分省略
}
为了在适当的位置插入这两个接收器,可以使用下面的上下文属性:
using System.Runtime.Remoting.Contexts;
using System.Threading;
public class TimeingProperty:IcontextProperty,IcontributeServerContextSink,IcontributeClientContextSink
{
ImessageSink GetServerContextSink(ImessageSink next){
return new ServerTimingSInk(next);
}
ImessageSink GetClientContextSink(ImessageSink next){
return new ClientTimingSink(next);
}
//IcontextProperty成员
public string Name{get{return “TimingProperty”;}}
public bool IsNewContextOK(Context ctx){
return true;
}
public void Freeze(Context ctx){}
}
调用GetServerContextSink方法是为了当前上下文内部的所有代理。CLR将为当前上下文外部的所有代理调用GetClientContextSink方法。^A