Correlação duplex durável

A correlação duplex durável, também conhecida como correlação de retorno de chamada, é útil quando um serviço de fluxo de trabalho tem o requisito de enviar um retorno de chamada para o chamador inicial. Ao contrário do WCF duplex, o retorno de chamada pode acontecer a qualquer momento no futuro e não está vinculado ao mesmo canal ou ao tempo de vida do canal. O único requisito é que o chamador tenha um ponto de extremidade ativo ouvindo a mensagem de retorno de chamada. Isso permite que dois serviços de fluxo de trabalho se comuniquem em uma conversa de longa execução. Este artigo fornece uma visão geral da correlação duplex durável.

Usar a correlação duplex durável

Para usar a correlação duplex durável, os dois serviços devem empregar uma associação habilitada para contexto que dê suporte a operações bidirecionais, como NetTcpContextBinding ou WSHttpContextBinding. O serviço de chamada registra um ClientCallbackAddress com a associação desejada em seu cliente Endpoint. O serviço de recebimento recebe esses dados na chamada inicial e, em seguida, usa-os no próprio Endpoint na atividade Send, que faz a chamada de retorno para o serviço de chamada. Neste exemplo, dois serviços se comunicam entre si. O primeiro serviço invoca um método no segundo serviço e aguarda a resposta. O segundo serviço sabe o nome do método de retorno de chamada, mas o ponto de extremidade do serviço que implementa esse método não é conhecido no tempo de design.

Observação

O duplex durável só pode ser usado quando o AddressingVersion do ponto de extremidade é configurado com WSAddressing10. Se não o for, uma exceção InvalidOperationException será gerada com a seguinte mensagem: "A mensagem contém um cabeçalho do contexto do retorno de chamada com uma referência de ponto de extremidade para AddressingVersion. O contexto do retorno de chamada só poderá ser transmitido quando o AddressingVersion estiver configurado com ”WSAddressing10”."

No exemplo a seguir, é hospedado um serviço de fluxo de trabalho que cria um retorno de chamada Endpoint usando 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);

O fluxo de trabalho que implementa esse serviço de fluxo de trabalho inicializa a correlação de retorno de chamada com sua atividade Send e faz referência a esse ponto de extremidade de retorno de chamada da atividade Receive que se correlaciona com o Send. O exemplo a seguir representa o fluxo de trabalho que retorna do método 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"
        }
     }
};

O segundo serviço de fluxo de trabalho é hospedado usando uma associação baseada em contexto fornecida pelo sistema.

// 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);

O fluxo de trabalho que implementa esse serviço de fluxo de trabalho começa por uma atividade Receive. Essa atividade de recebimento inicializa a correlação do retorno de chamada para esse serviço, atrasa por um período para simular o trabalho de execução longa e, em seguida, chama de volta para o primeiro serviço usando o contexto do retorno de chamada que foi passado na primeira chamada para o serviço. O exemplo a seguir representa o fluxo de trabalho que retorna de uma chamada para GetWF2. A atividade Send tem um endereço de espaço reservado http://www.contoso.com; o endereço real usado no tempo de execução é o endereço de retorno de chamada fornecido.

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"
        }
     }
};

Quando o método StartOrder é invocado no primeiro fluxo de trabalho, é exibida a saída a seguir, que mostra o fluxo de execução por meio dos dois fluxos de trabalho.

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

Neste exemplo, ambos os fluxos de trabalho gerenciam explicitamente a correlação usando um CallbackCorrelationInitializer. Como havia apenas uma única correlação nesses fluxos de trabalho de exemplo, o gerenciamento padrão CorrelationHandle teria sido suficiente.