Поделиться через


Устойчивая дуплексная корреляция

Устойчивая дуплексная корреляция, также известная как корреляция обратного вызова, полезна, если служба рабочего процесса имеет требование отправить обратный вызов исходному вызывающому объекту. В отличие от дуплексного WCF, обратный вызов может произойти в любой момент в будущем и не привязан к одному каналу или времени существования канала. Единственное требование заключается в том, что вызывающий должен иметь активную конечную точку соединения, прослушивающую сообщение обратного вызова. Это позволяет двум службам рабочих процессов взаимодействовать в длительной беседе. В этой статье представлен обзор устойчивой дуплексной корреляции.

Использование устойчивой дуплексной корреляции

Чтобы использовать устойчивую дуплексную корреляцию, две службы должны использовать привязку с поддержкой контекста, которая поддерживает двустороннюю операцию, например NetTcpContextBinding или WSHttpContextBinding. Вызывающая служба регистрирует ClientCallbackAddress с желаемой привязкой на их клиенте Endpoint. Получающая служба получает эти данные в первоначальном вызове, а затем использует их самостоятельно Endpoint в Send активности, которая возвращает обратный вызов вызывающей службе. В этом примере две службы взаимодействуют друг с другом. Первая служба вызывает метод во второй службе, а затем ожидает ответа. Вторая служба знает имя метода обратного вызова, но конечная точка службы, реализующая этот метод, не известна во время разработки.

Замечание

Устойчивый дуплекс можно использовать только в случае, если AddressingVersion конечной точки настроен с помощью WSAddressing10. Если это не так, InvalidOperationException исключение создается со следующим сообщением: "Сообщение содержит заголовок контекста обратного вызова с ссылкой на конечную точку для 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 было бы достаточно.