好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

以上下文(Context)的形式创建一个共享数据的容器

以上下文(Context)的形式创建一个共享数据的容器

以上下文(Context)的形式创建一个共享数据的容器

在很多情况下我们具有这样的需求:为一组相关的操作创建一个执行上下文并提供一个共享的数据容器,而不是简单地定义一个全局变量,或者将数据通过参数传来传去。这样的上下文一般具有其生命周期,它们在目标操作开始执行的时候被激活,在执行完成之后被回收。该上下文一般不能跨越多个线程,以避免多个线程操作相同的数据容器造成数据的不一致。针对这个需求,我们写了一个非常简单的例子,有兴趣的朋友可以看看。[源代码从 这里 下载]

目录 
一、ExecutionContext的基本编程方式 
二、异步调用的问题 
三、ExecutionContext 
四、DependentExecutionContext 
五、ExecutionContextScope

一、ExecutionContext的基本编程方式

我将这个作为数据容器的上下文命名为ExecutionContext,我完全借鉴了TransactionScope的编程方式来设计这个ExecutionContext。如下的代码片段体现了ExecutionContext最基本的编程方式:我们通过ExecutionContextScope 来创建当前ExecutionContext,并且控制它的生命周期。当前ExecutionContext通过静态属性Current获取。我们分别调用GetValue和SaveValue进行上下文数据项的获取和设置。

 using  (ExecutionContextScope contextScope =  new  ExecutionContextScope())
{
     //Set 
    ExecutionContext.Current.SetValue(“ActivityID”, “A001”);
     //Get 
     string  activityId = ExecutionContext.Current.GetValue< string >(“ActivityID”)
}

和TransactionScope一样,ExecutionContextScope 也支持嵌套。具体来说,当我们采用嵌套的ExecutionContextScope 时,有对应着如下三种不同的上下文共享行为:

Required: 外层的ExecutionContext直接被内层使用; RequiresNew:内层创建一个全新的ExecutionContext; Suppress:外层的ExecutionContext在内层中使被屏蔽掉,内层的当前ExecutionContext不存在。

如下的代码片段反映了嵌套使用ExecutionContextScope 的编程方式,上述的三种行为通过作为ExecutionContextScope构造函数参数的ExecutionContextOption枚举来控制。

 using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
{
     //... 
     using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption. Required ))
    {
         //... 
    }
     using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption. RequiresNew ))
    {
         //... 
    }
     using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption. Suppress ))
    {
         //... 
    }
}

ExecutionContext基本的编程方式,以及三种ExecutionContextScope 嵌套所体现的ExecutionContext创建/共享机制可以通过如下的Unit Test代码来体现:

[TestMethod]
 public   void  SetAndGetContexts1()
{
     string  name = Guid.NewGuid().ToString();
     string  value1 = Guid.NewGuid().ToString();
     string  value2 = Guid.NewGuid().ToString();

     //1. Outside of ApplicationContextScope: ApplicationContext.Current = null 
    Assert.IsNull(ExecutionContext.Current);

     //2. Current ApplicationContext is avilable in the ApplicationContextScope. 
     using  (ExecutionContextScope contextScope =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
        Assert.AreEqual< string >(value1, ExecutionContext.Current.GetValue< string >(name));
    }

     //3. Nested ApplicationContextScope: ApplicationContextOption.Required 
     using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
         using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption.Required))
        {
            Assert.AreEqual< string >(value1, ExecutionContext.Current.GetValue< string >(name));

            ExecutionContext.Current.SetValue(name, value2);
            Assert.AreEqual< string >(value2, ExecutionContext.Current.GetValue< string >(name));
        }
        Assert.AreEqual< string >(value2, ExecutionContext.Current.GetValue< string >(name));
    }


     //4. Nested ApplicationContextScope: ApplicationContextOption.RequiresNew 
     using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
         using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption.RequiresNew))
        {
            Assert.IsNotNull(ExecutionContext.Current);
            Assert.IsNull(ExecutionContext.Current.GetValue< string >(name));
            ExecutionContext.Current.SetValue(name, value2);
            Assert.AreEqual< string >(value2, ExecutionContext.Current.GetValue< string >(name));
        }
        Assert.AreEqual< string >(value1, ExecutionContext.Current.GetValue< string >(name));
    }

     //5. Nested ApplicationContextScope: ApplicationContextOption.Supress 
     using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
         using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(ExecutionContextOption.Suppress))
        {
            Assert.IsNull(ExecutionContext.Current);
        }
        Assert.AreEqual< string >(value1, ExecutionContext.Current.GetValue< string >(name));
    }
}
二、异步调用的问题

如果具有当前ExecutionContext的程序以异步的方式执行相应的操作,我们希望当前操作和异步操作使用不同的数据容器,否则就会出现并发问题;但是我们又希望在异步操作开始执行的时候,当前的上下文数据能够自动地拷贝过去。为此我们依然借鉴TransactionScope的方式,定义了一个DependentContext(对应着DependentTransaction)。在异步操作开始执行之前,我们根据当前ExecutionContext创建一个DependentContext,此时当前ExecutionContext相应数据项会拷贝到DependentContext中。在异步操作代码中,我们根据DependentContext创建ExecutionContextScope ,那么通过Current属性返回的实际上就是这么一个DependentContext。由于DependentContext和当前ExecutionContext各自具有自己的数据容器,针对它们的操作互不影响。如下所示的相应的编程方式:

 using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
{
    ExecutionContext.Current.SetValue(name, value1);
     DependentContext depedencyContext = ExecutionContext.Current.DepedentClone(); 
    ExecutionContext.Current.SetValue(name, value2);

     Task.Factory.StartNew(() =>
        {
             using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope( depedencyContext ))
            {
                  string  value1 = ExecutionContext.Current.GetValue< string >(name);
            }
        });
}

相应的编程方式,已经异步线程和当前线程上下文的独立性也可以通过如下所示的Unit Test代码来体现。

[TestMethod]
 public   void  SetAndGetContexts2()
{
     string  name = Guid.NewGuid().ToString();
     string  value1 = Guid.NewGuid().ToString();
     string  value2 = Guid.NewGuid().ToString();

     //1. Change current ApplicationContext will never affect the DependentContext. 
     using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
        DependentContext depedencyContext = ExecutionContext.Current.DepedentClone();
        ExecutionContext.Current.SetValue(name, value2);

        Task< string > task = Task.Factory.StartNew< string >(() =>
            {
                 using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(depedencyContext))
                {
                     return  ExecutionContext.Current.GetValue< string >(name);
                }
            });

        Assert.AreEqual< string >(value1, task.Result);
        Assert.AreEqual< string >(value2, ExecutionContext.Current.GetValue< string >(name));
    }

     //2. Change DependentContext will never affect the current ApplicationContext. 
     using  (ExecutionContextScope contextScope1 =  new  ExecutionContextScope())
    {
        ExecutionContext.Current.SetValue(name, value1);
        DependentContext depedencyContext = ExecutionContext.Current.DepedentClone();
        Task< string > task = Task.Factory.StartNew< string >(() =>
        {
             using  (ExecutionContextScope contextScope2 =  new  ExecutionContextScope(depedencyContext))
            {
                ExecutionContext.Current.SetValue(name, value2);
                 return  ExecutionContext.Current.GetValue< string >(name);
            }
        });

        Assert.AreEqual< string >(value2, task.Result);
        Assert.AreEqual< string >(value1, ExecutionContext.Current.GetValue< string >(name));
    }
}
三、ExecutionContext

现在我们来讨论具体的设计和实现,先来看看表示当前执行上下文的ExecutionContext的定义。如下面的代码片段所示,ExecutionContext实际上是利用了通过Items属性表示的字典对象作为保存数据的容器,GetValue和SetValue实际上就是针对该字典的操作。表示当前ExecutionContext的静态属性Current实际上是返回一个应用了ThreadStaticAttribute特性的静态字段current,意味着ExecutionContext是基于某个线程的,每个线程的当前ExecutionContext是不同的。方法DepedentClone用于创建DependentContext 以实现当前上下文数据向异步线程的传递。

[Serializable]
 public   class  ExecutionContext
{
     [ThreadStatic] 
     private   static  ExecutionContext current;
     public  IDictionary< string ,  object > Items { get;  internal  set; }
     internal  ExecutionContext()
    {
         this .Items =  new  Dictionary< string ,  object >();
    }

     public  T GetValue<T>( string  name, T defaultValue =  default (T))
    {
         object   value ;
         if  ( this .Items.TryGetValue(name,  out   value ))
        {
             return  (T) value ;
        }
         return  defaultValue;
    }
     public   void  SetValue( string  name,  object   value )
    {
         this .Items[name] =  value ;
    }

     public   static  ExecutionContext Current
    {
        get {  return  current; }
         internal  set { current =  value ; }
    }
     public  DependentContext DepedentClone()
    {
         return   new  DependentContext( this );
    }
}
四、DependentExecutionContext

如下所示的DependentContext的定义,它是ExecutionContext的子类。我们我们根据指定的ExecutionContext 对象创建一个DependentContext对象的时候,它的上下文数据项会自动拷贝到创建的DependentContext之中。

[Serializable]
 public   class  DependentContext: ExecutionContext
{
     public  Thread OriginalThread { get;  private  set; }
     public  DependentContext(ExecutionContext context)
    {
       this.OriginalThread = Thread.CurrentThread;
this .Items = new Dictionary< string , object >(context.Items); } }
五、ExecutionContextScope

如下所示的是ExecutionContextScope的定义,它实现了IDisposable接口。在ExecutionContextScope被创建之前,当前ExecutionContext 被保存下来。第一个构造函数根据指定的ExecutionContextOption来对当前ExecutionContext 进行相应的设置;第二个构造函数则直接将指定的DependentContext 作为当前的ExecutionContext 。

 public   class  ExecutionContextScope:IDisposable
{
     private  ExecutionContext originalContext = ExecutionContext.Current;
     public  ExecutionContextScope(ExecutionContextOption contextOption = ExecutionContextOption.Required)
    {
         switch  (contextOption)
        {
             case  ExecutionContextOption.RequiresNew:
                {
                    ExecutionContext.Current =  new  ExecutionContext();
                     break ;
                }
             case  ExecutionContextOption.Required:
                {
                    ExecutionContext.Current = originalContext ??  new  ExecutionContext();
                     break ;
                }
             case  ExecutionContextOption.Suppress:
                {
                    ExecutionContext.Current =  null ;
                     break ;
                }
        }
    }
     public  ExecutionContextScope(DependentContext dependentContext)
    {
         if  (dependentContext.OriginalThread == Thread.CurrentThread)
        {
             throw   new  InvalidOperationException( "The DependentContextScope cannot be created in the thread in which the DependentContext is created." );
        }
        ExecutionContext.Current = dependentContext;
    }
     public   void  Dispose()
    {
        ExecutionContext.Current = originalContext;
    }
}

作者: Artech
出处: http://artech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

 

分类:  [14] 框架设计

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于以上下文(Context)的形式创建一个共享数据的容器的详细内容...

  阅读:37次