.NET 上下文拦截 转


在.NET 中,上下文(Context)的概念贯穿着.NET的很多核心内容.他提供了类似运行环境的服务和内容.在.NET中,基于上下文拦截的技术一直是很隐晦的,没有很多的文档说明。然儿,基于上下文的拦截的技术却提供了很好很强大的功能。使我们能方便是实现类似 AOP 的编程模式。.NET3.0 中的WCF更是大量使用了上下文拦截技术。
    在.NET 中,组件服务通常的颗粒是域(Domain)和上下文(Context).其中,在域中提供的服务通常只有'同步域'.当然,.NET 中也提供了很多COM+服务,但需要被服务的组件(类)直接或间接继承自 ServicedComponent(被服务的组件)..NET中提供的COM+服务全部在命名空间System.EnterpriseServices中,在其中,提供了诸如消息队列,即时激活,对象池...等服务.
    在使用.NET 开发时,我们都知道我们使用任何一种.NET支持的语言编写的代码都是托管代码.所谓托管代码生成的IL语言只能运行在CLR中.并且享受CLR提供的垃圾回收,托管堆...等.但是,Windows在为我们的IL运行提供一个进程时,并不知道所谓的托管环境,Windows只负责提供进程,在进程中没有托管环境的概念.在Windows 进程和IL之间,域提供了桥梁的作用。在Windows进程中,域提供了诸如垃圾回收,托管堆,和JIT,加载和卸载程序集等服务。也就是说,我们所有的IL代码必须运行在域中,当然,当我们启动一个.NET程序时,会创建一个默认的域来加载和运行我们的.NET程序,并提供托管堆。我们也可以创建新的域。所以,每个Windows 进程可以宿主多个域。在.NET中,对象是不能够跨域直接引用的,每个.NET对象都是他所属的域中的托管堆被创建的。对象的跨域访问,必须使用代理(Proxy),并且对象是可以被封送的(直接或间接继承自MarshalByRefObject),跨域访问对象就是所谓的远程访问(Remoting)。
    在.NET中域的粒度仍然很大,如果我们不显式的创建新的域,通常我们程序中的所有组件(类),都运行在同一个域中。在.NET中,上下文(Context),提供了比域粒度更小的运行环境。域可以有多个上下文,但一个上下文只能属于一个域。
和域一样,我们的 .NET组件(类)也必须运行在不同的上下文中,只不过不同的上下文提供不同的上下文服务,每个域中也有一个默认的上下文,默认的上下文但是不提供任何的服务。上下文最大的好处是让我们可以使用上下文拦截技术,为被宿主在上下文的组件(类)提供自定义的服务。这样,我们就可以实现所谓AOP编程。我们可以把AOP中所谓的管道工作作成服务,添加到上固定的下文服务中,这样每个对这个上下文中的组件(类)的调用,都将被拦截,然后把服务(管道方法)添加到组件(类),就好象把服务(管道方法)织入(AOP术语)到组件(类)中.



    在 .NET 中,默认的对象都是上下文敏感对象,他们会被放置在引用他们的宿主的上下文中,如果我们想使用上下文服务,必须使我们的对象放置在提供特定的服务的上下文中,这一类型的对象是面向上下文的对象,这些对象的类必须直接或间接继承自 ContextBoundObject类.面向上下文对象在创建时会检查创建宿主的上下文是否提供他所需要的服务,如果宿主不提供需要的服务,那么.NET 将首先创建一个可以提供需要服务的上下文,然后在新创建的上下文中创建对象.
    在.NET 中,跨上下文的访问和Remoting很相似.他们都不直接引用创建的代理,而是使用代理访问另一个上下文中对象.
通常的情况是 客户端 ->透明代理 ->真实代理 ->客户端接收器 ->信道 ->服务器端接收器->堆栈构造 ->面向上下文对象.通过这个调用链,我们可以发现,在跨上下文调用中没有Remoting 中使用的格式器,并且跨上下文调用使用的信道也和Remoting 中的信道不一样,在跨上下文调用中使用CrossContextChannel信道.
    上面的调用链并不完整(大家有兴趣可以查看相关资料),但通过上面的调用链已经可以让我们大致了解.NET 的上下文拦截框架.在.NET中我们可以在客户段接收器和服务端接受器上加入我们自己的消息接收器来实现消息拦截.
.NET为了支持添加消息接收器,提供了类似装饰器的模式,我们在自己的消息接收器类中必须维持一个对下一个消息接收器的引用.
    下面我们就实现一个简单的拦截.为了更直观的展示拦截技术,我们只实现极其简单的功能,就是把方法的调用参数和返回值打印出来,当然打印的方法并不是在客户端,而是在消息接收器中,客户代码和正常代码没有任何区别.
    首先我们要实现接收器.代码如下
   
public   class  DemoSink : IMessageSink
    
{
        
private IMessageSink m_NextSink;

        
public DemoSink(IMessageSink nextSink)
        
{
            m_NextSink 
= nextSink;
        }


        
IMessageSink 成员#region IMessageSink 成员
        
public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
        
{
            
throw new NotImplementedException();
        }


        
public IMessageSink NextSink
        
{
            
get return m_NextSink; }
        }


        
public IMessage SyncProcessMessage(IMessage msg)
        
{
            IMethodMessage message 
= msg as IMethodMessage;
            Type type 
= Type.GetType(message.TypeName);
            
string text = type.Name + "." + message.MethodName;
            Debug.WriteLine(text);
            
foreach (object obj in message.Args)
            
{
                Debug.WriteLine(obj.ToString());
            }

            
            IMethodReturnMessage returnMessage 
= (IMethodReturnMessage)m_NextSink.SyncProcessMessage(msg);
            Debug.WriteLine(returnMessage.ReturnValue.ToString());
            
return returnMessage;
        }

        
#endregion

    }
    通过上面的代码可以看出,消息接收器的实现很简单.我们在同步方法调用中,在方法调用前和调用后插入了一些处理,把方法调用参数和返回值打印了出来.
    在实现了消息接收器后,我们还要添加两个类,用于把接收器添加到消息接收链中.
    首先我们要创建一个自定义特性( Attribute)类,使用自定义特性来标注使用上下文拦截是组件(类).这个特性类不能直接继承自Attribute 类,必须继承自 ContextAttribute 类,然后还要创建一个实现接口 IContextPropertyIContributeServerContextSink的类.
    两个类的代码如下
   
[AttributeUsage(AttributeTargets.Class)]
    
public   class  DemoContextAttribute : ContextAttribute
    
{
        
public DemoContextAttribute()
            : 
base("DemoContextAttribute")
        
{
        }


        
public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)
        
{
            DemoProperty proerty 
= new DemoProperty();
            ctorMsg.ContextProperties.Add(proerty);
        }

        
public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
        
{
            DemoProperty property 
= ctx.GetProperty("DemoTest"as DemoProperty;
            
if (property == nullreturn false;
            
else return true;
        }

    }

public   class  DemoProperty : IContextProperty, IContributeServerContextSink
    
{
        
public DemoProperty()
        
{
        }


        
IContextProperty 成员#region IContextProperty 成员
        
public void Freeze(Context newContext)
        
{
        }

        
public bool IsNewContextOK(Context newCtx)
        
{
            DemoProperty property 
= newCtx.GetProperty("DemoTest"as DemoProperty;
            
if (property == nullreturn false;
            
else return true;
        }

        
public string Name
        
{
            
get return "DemoTest"; }
        }

        
#endregion



        
IContributeServerContextSink 成员#region IContributeServerContextSink 成员

        
public IMessageSink GetServerContextSink(IMessageSink nextSink)
        
{
            IMessageSink sink 
= new DemoSink(nextSink);
            
return sink;
        }


        
#endregion

    }

然后我们就可以为指定的组件(类)添加上下文服务了
[DemoContext]
    class Class1 : ContextBoundObject
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }

客户端代码和没有任何改变
class Program
    {
        static void Main(string[] args)
        {
            Class1 c = new Class1();
            int a = c.Add(1, 2);
        }
    }

    上面的代码只是演示上下文拦截技术的实现,在实际编程中,我们可以使用上下文拦截实现把日志管理,角色权限管理,事务管理等一些管道工作从我们的组件(类)代码中剥离,使我们的组件(类)只关注服务的实现,而不需要去关心这些管道工作.

转载于:https://www.cnblogs.com/yanbinboy/archive/2008/05/20/1202989.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值