永続的な二重の相関関係

永続的な二重の相関関係 (コールバック相関関係) は、ワークフロー サービスがコールバックを最初の呼び出し元に送信する必要がある場合に便利です。 WCF 二重とは異なり、コールバックは将来の任意のタイミングで発生する可能性があり、同じチャネルやチャネルの有効期間に縛られることはありません。 唯一の要件は、コールバック メッセージをリッスンするアクティブなエンドポイントが呼び出し元にあることです。 このため、2 つのワークフロー サービスが長時間のメッセージ交換を使用して通信できます。 この記事では、永続的な二重の相関関係について概要を説明します。

永続的な二重の相関関係の使用

永続的な二重の相関関係を使用するには、2 つのサービスが、NetTcpContextBindingWSHttpContextBinding など、双方向の操作をサポートするコンテキスト対応バインドを使用する必要があります。 呼び出し側サービスは、クライアントの ClientCallbackAddress で使用するバインドを指定して Endpoint を登録します。 受信側サービスは、このデータを最初の呼び出しで受信し、呼び出し側サービスへのコールバックを行う Endpoint アクティビティにおいて、受信側サービス自体の Send でこのデータを使用します。 次の例では、2 つのサービスが互いに通信しています。 1 つ目のサービスは、2 つ目のサービスに対してメソッドを呼び出し、応答を待機します。 2 つ目のサービスでは、コールバック メソッドの名前が認識されていますが、このメソッドを実装するサービスのエンドポイントは、設計時には認識されていません。

Note

エンドポイントの AddressingVersionWSAddressing10 で構成される場合は、永続的な二重のみが使用できます。 そうでない場合は、InvalidOperationException 例外が次のメッセージとともにスローされます。"The message contains a callback context header with an endpoint reference for AddressingVersion. (メッセージに、AddressingVersion のエンドポイント参照を含むコールバック コンテキスト ヘッダーが含まれています。) Callback context can only be transmitted when the AddressingVersion is configured with 'WSAddressing10'." (コールバック コンテキストを送信できるのは、AddressingVersion で 'WSAddressing10' が構成されている場合のみです。)

次の例では、Endpoint を使用してコールバック WSHttpContextBinding を作成するワークフロー サービスがホストされます。

// Host WF Service 1.
string baseAddress1 = "http://localhost:8080/Service1";
WorkflowServiceHost host1 = new WorkflowServiceHost(GetWF1(), new Uri(baseAddress1));

// Add the callback endpoint.
WSHttpContextBinding Binding1 = new WSHttpContextBinding();
host1.AddServiceEndpoint("ICallbackItemsReady", Binding1, "ItemsReady");

// Add the service endpoint.
host1.AddServiceEndpoint("IService1", Binding1, baseAddress1);

// Open the first workflow service.
host1.Open();
Console.WriteLine("Service1 waiting at: {0}", baseAddress1);

このワークフロー サービスを実装するワークフローは、ワークフロー自体の Send アクティビティを使用してコールバック相関関係を初期化し、Receive と相関関係にある Send アクティビティからこのコールバック エンドポイントを参照します。 次の例は、GetWF1 メソッドから返されるワークフローです。

Variable<CorrelationHandle> CallbackHandle = new Variable<CorrelationHandle>();

Receive StartOrder = new Receive
{
    CanCreateInstance = true,
    ServiceContractName = "IService1",
    OperationName = "StartOrder"
};

Send GetItems = new Send
{
    CorrelationInitializers =
    {
        new CallbackCorrelationInitializer
        {
            CorrelationHandle = CallbackHandle
        }
    },
    ServiceContractName = "IService2",
    OperationName = "StartItems",
    Endpoint = new Endpoint
    {
        AddressUri = new Uri("http://localhost:8081/Service2"),
        Binding = new WSHttpContextBinding
        {
            ClientCallbackAddress = new Uri("http://localhost:8080/Service1/ItemsReady")
        }
    }
};

Receive ItemsReady = new Receive
{
    ServiceContractName = "ICallbackItemsReady",
    OperationName = "ItemsReady",
    CorrelatesWith = CallbackHandle,
};

Activity wf = new Sequence
{
    Variables =
    {
        CallbackHandle
    },
    Activities =
    {
        StartOrder,
        new WriteLine
        {
            Text = "WF1 - Started"
        },
        GetItems,
        new WriteLine
        {
            Text = "WF1 - Request Submitted"
        },
        ItemsReady,
        new WriteLine
        {
            Text = "WF1 - Items Received"
        }
     }
};

2 つ目のワークフロー サービスは、システムが提供するコンテキスト ベースのバインドを使用してホストされます。

// Host WF Service 2.
string baseAddress2 = "http://localhost:8081/Service2";
WorkflowServiceHost host2 = new WorkflowServiceHost(GetWF2(), new Uri(baseAddress2));

// Add the service endpoint.
WSHttpContextBinding Binding2 = new WSHttpContextBinding();
host2.AddServiceEndpoint("IService2", Binding2, baseAddress2);

// Open the second workflow service.
host2.Open();
Console.WriteLine("Service2 waiting at: {0}", baseAddress2);

このワークフロー サービスを実装するワークフローは、Receive アクティビティから開始されます。 この受信アクティビティは、このサービスのコールバック相関関係を初期化し、一定の遅延時間を設けて長時間の作業をシミュレーションして、サービスへの最初の呼び出しで渡されたコールバック コンテキストを使用して、最初のサービスにコールバックします。 次の例は、GetWF2 への呼び出しから返されるワークフローです。 Send アクティビティには、http://www.contoso.com のプレースホルダー アドレスが含まれています。実行時に使われる実際のアドレスは、指定されたコールバック アドレスです。

Variable<CorrelationHandle> ItemsCallbackHandle = new Variable<CorrelationHandle>();

Receive StartItems = new Receive
{
    CorrelationInitializers =
    {
        new CallbackCorrelationInitializer
        {
            CorrelationHandle = ItemsCallbackHandle
        }
    },
    CanCreateInstance = true,
    ServiceContractName = "IService2",
    OperationName = "StartItems"
};

Send ItemsReady = new Send
{
    CorrelatesWith = ItemsCallbackHandle,
    Endpoint = new Endpoint
    {
        // The callback address on the binding is used
        // instead of this placeholder address.
        AddressUri = new Uri("http://www.contoso.com"),

        Binding = new WSHttpContextBinding()
    },
    OperationName = "ItemsReady",
    ServiceContractName = "ICallbackItemsReady"
};

Activity wf = new Sequence
{
    Variables =
    {
        ItemsCallbackHandle
    },
    Activities =
    {
        StartItems,
        new WriteLine
        {
            Text = "WF2 - Request Received"
        },
        new Delay
        {
            Duration = TimeSpan.FromMinutes(90)
        },
        new WriteLine
        {
            Text = "WF2 - Sending items"
        },
        ItemsReady,
        new WriteLine
        {
            Text = "WF2 - Items sent"
        }
     }
};

StartOrder メソッドが最初のワークフローで呼び出されると、次の出力が表示されます。これは、2 つのワークフローの実行フローを示しています。

Service1 waiting at: http://localhost:8080/Service1
Service2 waiting at: http://localhost:8081/Service2
Press enter to exit.
WF1 - Started
WF2 - Request Received
WF1 - Request Submitted
WF2 - Sending items
WF2 - Items sent
WF1 - Items Received

この例では、どちらのワークフローも CallbackCorrelationInitializer を使用して相関関係を明示的に管理しています。 サンプル ワークフローには相関関係が 1 つしかないため、既定の CorrelationHandle で十分に管理できます。