共用方式為


永久性雙工相互關聯

永久性雙工相互關聯也稱為回呼相互關聯,在工作流程服務需要傳送回呼至初始呼叫端時相當實用。 不同於 WCF 雙工,回呼未來隨時都可能發生,而且不會繫結至相同的通道或通道存留期。 唯一的需求是呼叫端具有接聽回呼訊息的作用中端點。 如此可讓兩項工作流程服務在長時間執行的對話中彼此通訊。 本文提供永久性雙工相互關聯的概觀。

使用永久性雙工相互關聯

若要使用永久性雙工相互關聯,兩項服務必須使用支援雙向作業的啟用內容繫結,例如 NetTcpContextBindingWSHttpContextBinding。 呼叫服務會將 ClientCallbackAddress 註冊至其用戶端 Endpoint 上所需的繫結。 接收服務會在初始呼叫中接收這項資料,然後在對呼叫服務進行回呼的 Endpoint 活動中,於自己的 Send 上使用該資料。 在這個範例中,兩項服務會彼此通訊。 第一項服務會在第二項服務上叫用方法,然後等候回覆。 第二項服務知道回呼方法的名稱,但是在設計階段並不知道實作這個方法的服務端點。

注意

只有當端點的 AddressingVersion 是使用 WSAddressing10 來設定時,才能使用永久性雙工。 否則會擲回 InvalidOperationException 例外狀況,並顯示下列訊息:「訊息包含的回呼內容標頭具有 AddressingVersion 的端點參考。 只有當 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"
        }
     }
};

第二項工作流程服務是使用系統提供之以內容為主的繫結進行裝載。

// 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 方法時,會顯示下列輸出,其中顯示兩個工作流程之間執行的流向。

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 明確管理相互關聯。 由於上述這些範例工作流程中只有單一相互關聯,因此預設的 CorrelationHandle 管理就已經足夠。