Attributeの.NETプログラミングへの応用(六)

7020 ワード

(前節).NET Frameworkブロッキングメカニズムの設計では、クライアントとオブジェクトの間に複数のメッセージ受信機が存在し、これらのメッセージ受信機はチェーンテーブルを構成し、クライアントの呼び出しオブジェクトのプロセスと呼び出しが戻ってブロッキングを実行し、独自の消息受信機をカスタマイズし、チェーンテーブルに挿入して、呼び出しの前処理と後処理を完了することができます.では、呼び出しブロックはどのように構築されているのか、あるいはどのように実現されているのか.
.NETには、アプリケーションドメイン間(App Domain)とコンテキスト環境間(Context)の2つの呼び出しがあり、両方の呼び出しは中間エージェントを介して行われます.(proxy)エージェントは、透明エージェントと実際のエージェントの2つの部分に分けられます.透明エージェントは、同じオブジェクトのような共通のエントリポイントを露出し、クライアントが透明エージェントを呼び出すと、スタック内のフレームをメッセージ(前節で説明したIMessageインタフェースを実装するオブジェクト)に変換します.メソッド名やパラメータなどのプロパティセットを含むメッセージを実際のエージェントに渡し、次の2つのケースに分けます.アプリケーションドメイン間では、実際のエージェントがフォーマットを使用してメッセージをシーケンス化し、リモートチャネルに格納します.次の環境間では、実際のエージェントはチャネルフォーマット、チャネル、Contextブロッキングを知る必要はありません.メッセージを前に伝える前に呼び出しをブロックし、メッセージを処理した後(前処理)にメッセージを受信するメッセージ受信機にメッセージを渡すだけです.メッセージは次の受信機に渡され、チェーンテーブルの最後の受信機まで渡されます.最後の受信機はスタック作成器と呼ばれ、メッセージをスタックフレームに復元し、オブジェクトを呼び出します.呼び出し方法の結果が返されると、スタック作成器は結果をメッセージに変換し、呼び出したメッセージ受信機に返され、メッセージは元のチェーンテーブルに沿って戻ります.送信、各チェーンテーブル上のメッセージ受信機は、メッセージを返信する前にメッセージを後処理する.チェーンテーブルの最初の受信機まで、最初の受信機はメッセージを実際のエージェントに返信し、実際のエージェントはメッセージを透明なエージェントに伝え、後者はメッセージをクライアント側のスタックに戻す.上記の説明からContextを通り抜けるメッセージはフォーマットする必要がなく、CLRCrossContextChannelという内部チャネルを使用します.このオブジェクトもメッセージ受信機です.
いくつかのメッセージ受信機のタイプがあり、1つの呼び出しブロックはサーバー側でもクライアント側でも行うことができ、サーバー側受信機はサーバーコンテキスト環境におけるすべてのオブジェクトに対する呼び出しをブロックし、同時にいくつかの前処理と後処理を行う.クライアントの受信機はすべての外出クライアントコンテキスト環境の呼び出しをブロックし、同時にいくつかの前処理と後処理を行う.サーバはサーバ側受信機のインストールを担当し、サーバ側コンテキスト環境へのアクセスをブロックする受信機をサーバコンテキスト環境受信機と呼び、実際のオブジェクトを呼び出す受信機をオブジェクト受信機と呼ぶ.クライアントによってインストールされたクライアント受信機をゲスト側コンテキスト環境受信機と呼び、オブジェクトによってインストールされたクライアント受信機を特使と呼ぶ(Envoy)受信機、特に受信機はそれに関連するオブジェクトのみをブロックする.クライアントの最後の受信機とサーバー側の最初の受信機はCrossContextChannelタイプの例である.異なるタイプの受信機は異なるセグメントを構成し、各セグメントの端点にはターミネータと呼ばれる受信機が装着され、ターミネータはこのセグメントのメッセージを次のセグメントに伝える役割を果たす.サービスではServerContextTerminatorSinkは、ServerContextTerminatorSinkコンテキストセグメントの最後のターミネータです.ターミネータでNextSinkを呼び出すとnullが返され、デッドエンドのように動作しますが、次の受信オブジェクトのプライベートフィールドが内部に保存されます.
.NET Frameworkのオブジェクト呼び出しブロックの実装メカニズムを大まかに紹介しました.このメカニズムを認識させることを目的としています.今は私たちのコードを実装する時です.コードの実装を通じて、メッセージがどのように処理されるかを見ることができます.まず、私たちのプログラムのために受信機CallTraceSinkを定義します.
//TraceContext.cs



using System;

using System.Runtime.Remoting.Contexts;

using System.Runtime.Remoting.Messaging;

using System.Runtime.Remoting.Activation;



namespace NiwalkerDemo

{

   public class CallTraceSink : IMessageSink //  IMessageSink

   {

      private IMessageSink nextSink;  //        

      

      //              

      public CallTraceSink(IMessageSink next)

      {

         nextSink=next;

      }

      

      //     IMessageSink    

      public IMessageSink NextSink

      {

         get

         {

            return nextSink;

         }

      }

      

      //  IMessageSink     ,        ,      

      public IMessage SyncProcessMessage(IMessage msg)

      {

         //    ,    

         Preprocess(msg);

         //           

         IMessage retMsg=nextSink.SyncProcessMessage(msg);

         //         ,      

         Postprocess(msg,retMsg);

         return retMsg;

      }

      

      //IMessageSink    ,      ,         ,      null,

      //         ,         

      public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)

      {

         return null;

      }

      

      //        ,      ,       ,                  ,

      //       ,      Inventory            ,

      //  ,                 ,    NextSink    

      private void Preprocess(IMessage msg)

      {

         //         ,     Order Submit  。

         IMethodCallMessage call=msg as IMethodCallMessage;

         

         if(call==null)

            return;

         

         if(call.MethodName=="Submit")

         {

            string product=call.GetArg(0).ToString(); //  Submit        

            int qty=(int)call.GetArg(1); //  Submit        

            

            //  Inventory      

            if(new Inventory().Checkout(product,qty))

               Console.WriteLine("Order availible");

            else

            {

               Console.WriteLine("Order unvailible");

               SendEmail();

            }

          }

       }

       

       //     ,          ,              

       //       ,       

       private void Postprocess(IMessage msg,IMessage retMsg)

       {

          IMethodCallMessage call=msg as IMethodCallMessage;

          

          if(call==null)

             return;

          Console.WriteLine("Log order information");

       }

       

       private void SendEmail()

       {

          Console.WriteLine("Send email to manager");

       }

    }  

        ...  


次に、コンテキスト環境のプロパティを定義します.コンテキスト環境のプロパティは、作成する受信機のタイプに応じて対応するインタフェースを実装する必要があります.たとえば、サーバコンテキスト環境受信機が作成されている場合は、IContributeServerContextSinkインタフェースを実装する必要があります.
      ...

public class CallTraceProperty : IContextProperty, IContributeObjectSink

{

   public CallTraceProperty()

   {

   }

   

   //IContributeObjectSink     ,        

   public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink next)

   {

      return new CallTraceSink(next);

   }

   

   //IContextProperty    ,       ture,             

   public bool IsNewContextOK(Context newCtx)

   {

      return true;

   }

   

   //IContextProperty    ,      

   public void Freeze(Context newCtx)

   {

   }

   

   //IContextProperty    

   public string Name

   {

      get { return "OrderTrace";}

   }

}

         ...


最後はContextAttribute
  ...

   [AttributeUsage(AttributeTargets.Class)]

   public class CallTraceAttribute : ContextAttribute

   {

      public CallTraceAttribute():base("CallTrace")

      {

      }

      

      //  ContextAttribute  ,           

      public override void GetPropertiesForNewContext(IConstructionCallMessage ctorMsg)

      {

         ctorMsg.ContextProperties.Add(new CallTraceProperty());

      }

   }

}


Orderオブジェクトを呼び出すSubmitメソッドがどのようにブロックされるかを明らかにするために、Orderクラスを少し修正し、ContextBoundObjectの派生クラスとして設計します.
//Inventory.cs



//Order.cs

using System;



namespace NiwalkerDemo

{

   [CallTrace]

   public class Order : ContextBoundObject

   {

      ...

      public void Submit(string product, int quantity)

      {

         this.product=product;

         this.quantity=quantity;

      }

    ...

   }

}




クライアント呼び出しコード:
...

public class AppMain

{

   static void Main()

   {

      Order order1=new Order(100);

      order1.Submit("Item1",150);

      

      Order order2=new Order(101);

      order2.Submit("Item2",150);

   }

}

...


実行結果は、OrderのSwumitをブロックすることに成功したことを示しています.説明する必要があるのは、ここのコードはContextAttributeアプリケーションのプレゼンテーションとしてのみ使用され、太い線であることです.具体的な実践では、より精巧に設計することができます.
後記:Attributeについてもっと紹介しようと思っていたのですが、本当に言いたいことが多すぎました.他のテーマで議論させていただきます.このシリーズを辛抱強く読んでいただき、ありがとうございました.ここで紹介した内容があなたのプログラミング生涯に啓発されたら、私の光栄です.もう一度ありがとうございました.(全文完)