次の方法で共有


トランスポート: UDP 経由のカスタム トランザクションのサンプル

TransactionMessagePropertyUDPTransport サンプルは、Windows Communication Foundation (WCF) トランスポート拡張トランスポート: UDP サンプルに基づいています。 カスタム トランザクション フローをサポートするように UDP トランスポート サンプルを拡張し、 TransactionMessageProperty プロパティの使用方法を示します。

UDP トランスポート サンプルのコード変更

トランザクション フローを示すために、サンプルでは、 ICalculatorContract のサービス コントラクトを変更して、 CalculatorService.Add()のトランザクション スコープを要求します。 このサンプルでは、System.Guid操作のコントラクトに追加のAdd パラメーターも追加します。 このパラメーターは、クライアント トランザクションの識別子をサービスに渡すために使用されます。

class CalculatorService : IDatagramContract, ICalculatorContract
{
    [OperationBehavior(TransactionScopeRequired=true)]
    public int Add(int x, int y, Guid clientTransactionId)
    {
        if(Transaction.Current.TransactionInformation.DistributedIdentifier == clientTransactionId)
    {
        Console.WriteLine("The client transaction has flowed to the service");
    }
    else
    {
     Console.WriteLine("The client transaction has NOT flowed to the service");
    }

    Console.WriteLine("   adding {0} + {1}", x, y);
    return (x + y);
    }

    [...]
}

トランスポート: UDP サンプルでは、UDP パケットを使用して、クライアントとサービスの間でメッセージを渡します。 トランスポート: カスタム トランスポート サンプルでは、同じメカニズムを使用してメッセージを転送しますが、トランザクションがフローされると、エンコードされたメッセージと共に UDP パケットに挿入されます。

byte[] txmsgBuffer = TransactionMessageBuffer.WriteTransactionMessageBuffer(txPropToken, messageBuffer);

int bytesSent = this.socket.SendTo(txmsgBuffer, 0, txmsgBuffer.Length, SocketFlags.None, this.remoteEndPoint);

TransactionMessageBuffer.WriteTransactionMessageBuffer は、現在のトランザクションの伝達トークンをメッセージ エンティティとマージし、バッファーに配置する新しい機能を含むヘルパー メソッドです。

カスタム トランザクション フロー トランスポートの場合、クライアントの実装では、トランザクション フローが必要なサービス操作を認識し、この情報を WCF に渡す必要があります。 また、ユーザー トランザクションをトランスポート層に送信するメカニズムも必要です。 このサンプルでは、"WCF メッセージ インスペクター" を使用してこの情報を取得します。 ここで実装されているクライアント メッセージ インスペクターは、 TransactionFlowInspectorと呼ばれ、次のタスクを実行します。

  • 特定のメッセージ アクションに対してトランザクションをフローする必要があるかどうかを判断します (これは IsTxFlowRequiredForThisOperation()で行われます)。

  • トランザクションのフローが必要な場合は、 TransactionFlowPropertyを使用して現在のアンビエント トランザクションをメッセージにアタッチします (これは BeforeSendRequest()で行われます)。

public class TransactionFlowInspector : IClientMessageInspector
{
   void IClientMessageInspector.AfterReceiveReply(ref           System.ServiceModel.Channels.Message reply, object correlationState)
  {
  }

   public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
   {
       // obtain the tx propagation token
       byte[] propToken = null;
       if (Transaction.Current != null && IsTxFlowRequiredForThisOperation(request.Headers.Action))
       {
           try
           {
               propToken = TransactionInterop.GetTransmitterPropagationToken(Transaction.Current);
           }
           catch (TransactionException e)
           {
              throw new CommunicationException("TransactionInterop.GetTransmitterPropagationToken failed.", e);
           }
       }

      // set the propToken on the message in a TransactionFlowProperty
       TransactionFlowProperty.Set(propToken, request);

       return null;
    }
  }

  static bool IsTxFlowRequiredForThisOperation(String action)
 {
       // In general, this should contain logic to identify which operations (actions)      require transaction flow.
      [...]
 }
}

TransactionFlowInspector自体は、カスタム動作 (TransactionFlowBehavior) を使用してフレームワークに渡されます。

public class TransactionFlowBehavior : IEndpointBehavior
{
       public void AddBindingParameters(ServiceEndpoint endpoint,            System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
      {
      }

       public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
      {
            TransactionFlowInspector inspector = new TransactionFlowInspector();
            clientRuntime.MessageInspectors.Add(inspector);
      }

      public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
     {
     }

      public void Validate(ServiceEndpoint endpoint)
      {
      }
}

前のメカニズムが設定された状態で、ユーザー コードはサービス操作を呼び出す前に TransactionScope を作成します。 メッセージ インスペクタは、トランザクションをサービス操作にフローする必要がある場合に、トランザクションがトランスポートに渡されるようにします。

CalculatorContractClient calculatorClient = new CalculatorContractClient("SampleProfileUdpBinding_ICalculatorContract");
calculatorClient.Endpoint.Behaviors.Add(new TransactionFlowBehavior());

try
{
       for (int i = 0; i < 5; ++i)
      {
              // call the 'Add' service operation under a transaction scope
             using (TransactionScope ts = new TransactionScope())
             {
        [...]
        Console.WriteLine(calculatorClient.Add(i, i * 2));
         }
      }
       calculatorClient.Close();
}
catch (TimeoutException)
{
    calculatorClient.Abort();
}
catch (CommunicationException)
{
    calculatorClient.Abort();
}
catch (Exception)
{
    calculatorClient.Abort();
    throw;
}

クライアントから UDP パケットを受信すると、サービスはそれを逆シリアル化してメッセージを抽出し、場合によってはトランザクションを抽出します。

count = listenSocket.EndReceiveFrom(result, ref dummy);

// read the transaction and message                       TransactionMessageBuffer.ReadTransactionMessageBuffer(buffer, count, out transaction, out msg);

TransactionMessageBuffer.ReadTransactionMessageBuffer() は、 TransactionMessageBuffer.WriteTransactionMessageBuffer()によって実行されるシリアル化プロセスを反転させるヘルパー メソッドです。

トランザクションが流入した場合は、TransactionMessagePropertyのメッセージに追加されます。

message = MessageEncoderFactory.Encoder.ReadMessage(msg, bufferManager);

if (transaction != null)
{
       TransactionMessageProperty.Set(transaction, message);
}

これにより、ディスパッチャーはディスパッチ時にトランザクションを取得し、メッセージによってアドレス指定されたサービス操作を呼び出すときにトランザクションを使用します。

サンプルを設定、ビルド、実行するには

  1. ソリューションをビルドするには、「 Windows Communication Foundation サンプルのビルド」の手順に従います。

  2. 現在のサンプルは、 トランスポート: UDP サンプルと同様に実行する必要があります。 これを実行するには、UdpTestService.exeを使用してサービスを開始します。 Windows Vista を実行している場合は、管理者特権でサービスを開始する必要があります。 これを行うには、エクスプローラーで UdpTestService.exe を右クリックし、[ 管理者として実行] をクリックします。

  3. これにより、次の出力が生成されます。

    Testing Udp From Code.
    Service is started from code...
    Press <ENTER> to terminate the service and start service from config...
    
  4. 現時点では、UdpTestClient.exeを実行してクライアントを起動できます。 クライアントによって生成される出力は次のとおりです。

    0
    3
    6
    9
    12
    Press <ENTER> to complete test.
    
  5. サービスの出力は次のとおりです。

    Hello, world!
    Hello, world!
    Hello, world!
    Hello, world!
    Hello, world!
    The client transaction has flowed to the service
       adding 0 + 0
    The client transaction has flowed to the service
       adding 1 + 2
    The client transaction has flowed to the service
       adding 2 + 4
    The client transaction has flowed to the service
       adding 3 + 6
    The client transaction has flowed to the service
       adding 4 + 8
    
  6. サービス アプリケーションは、クライアントによって送信されたトランザクション識別子 (The client transaction has flowed to the service操作の clientTransactionId パラメーター) とサービス トランザクションの識別子と一致できる場合に、メッセージ CalculatorService.Add()を表示します。 クライアント トランザクションがサービスにフローした場合にのみ、一致が取得されます。

  7. 構成を使用して発行されたエンドポイントに対してクライアント アプリケーションを実行するには、サービス アプリケーション ウィンドウで Enter キーを押し、テスト クライアントをもう一度実行します。 サービスに次の出力が表示されます。

    Testing Udp From Config.
    Service is started from config...
    Press <ENTER> to terminate the service and exit...
    
  8. サービスに対してクライアントを実行すると、以前と同様の出力が生成されるようになりました。

  9. Svcutil.exeを使用してクライアント コードと構成を再生成するには、サービス アプリケーションを起動し、サンプルのルート ディレクトリから次の Svcutil.exe コマンドを実行します。

    svcutil http://localhost:8000/udpsample/ /reference:UdpTransport\bin\UdpTransport.dll /svcutilConfig:svcutil.exe.config
    
  10. Svcutil.exe では、 sampleProfileUdpBindingのバインド拡張機能の構成は生成されません。手動で追加する必要があります。

    <configuration>
        <system.serviceModel>
            …
            <extensions>
                <!-- This was added manually because svcutil.exe does not add this extension to the file -->
                <bindingExtensions>
                    <add name="sampleProfileUdpBinding" type="Microsoft.ServiceModel.Samples.SampleProfileUdpBindingCollectionElement, UdpTransport" />
                </bindingExtensions>
            </extensions>
        </system.serviceModel>
    </configuration>
    

こちらも参照ください