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


На основе содержимого

Данный раздел относится к версии Windows Workflow Foundation 4.

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

Использование корреляции на основе содержимого

Корреляция на основе содержимого применяется, если служба рабочего процесса имеет несколько методов, доступных одному клиенту, и некоторые данные в сообщениях идентифицируют нужный экземпляр.

Ee358755.note(ru-ru,VS.100).gifПримечание
Корреляция на основе содержимого полезна, если нельзя применить корреляцию на основе контекста, так как привязка не относится ни к одному поддерживаемому типу привязок для обмена контекстом. Дополнительные сведения корреляции на основе контекста см. в разделе Обмен контекстом.

В каждом действии по обмену сообщениями при обмене данными должно быть указано расположение в сообщении данных, уникальным образом идентифицирующих экземпляр. Это делается путем указания набора MessageQuerySet с помощью QueryCorrelationInitializer или CorrelatesOn, выбирающих из сообщения данные, уникальным образом идентифицирующие экземпляр.

Ee358755.Warning(ru-ru,VS.100).gif Внимание!
Данные, используемые для идентификации экземпляра, хэшируются в ключ корреляции. Необходимо убедиться, что данные, используемые для корреляции, являются уникальными. В противном случае в хэшированном ключе могут возникнуть конфликты, которые приведут к неправильной маршрутизации сообщения. Например, корреляция на основе только имени заказчика может вызвать конфликт, так как могут иметься заказчики с одинаковыми именами. Двоеточие (:) не должно встречаться в данных, используемых для корреляции сообщений, так как оно уже применяется в качестве разделителя ключа и значения в запросе к сообщению, формирующих строку, которая впоследствии хэшируется.

В следующем примере начальное действие Receive/SendReply в службе рабочего процесса возвращает OrderId, который затем передается обратно клиенту при вызове следующего действия Receive в службе рабочего процесса.

Variable<string> OrderId = new Variable<string>();
Variable<string> Item = new Variable<string>();
Variable<CorrelationHandle> OrderIdHandle = new Variable<CorrelationHandle>();

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

SendReply ReplyToStartOrder = new SendReply
{
    Request = StartOrder,
    Content = SendParametersContent.Create(new Dictionary<string, InArgument>
        { { "OrderId", new InArgument<string>((env) => OrderId.Get(env)) } }),
    CorrelationInitializers =
    {
        new QueryCorrelationInitializer
        {
            CorrelationHandle = OrderIdHandle,
            MessageQuerySet = new MessageQuerySet
            {
                {
                    "OrderId", 
                    new XPathMessageQuery("sm:body()/tempuri:StartOrderResponse/tempuri:OrderId")
                }
            }
        }
    }
};

Receive AddItem = new Receive
{
    ServiceContractName = "IOrderService",
    OperationName = "AddItem",
    CorrelatesWith = OrderIdHandle,
    CorrelatesOn = new MessageQuerySet
    {
        {
            "OrderId", 
              new XPathMessageQuery("sm:body()/tempuri:AddItem/tempuri:OrderId")
        }
    },
    Content = ReceiveParametersContent.Create(new Dictionary<string, OutArgument>
        { { "OrderId", new OutArgument<string>(OrderId) },  
        { "Item", new OutArgument<string>(Item) } })
};

SendReply ReplyToAddItem = new SendReply
{
    Request = AddItem,
    Content = SendParametersContent.Create(new Dictionary<string, InArgument>
        { { "Reply", new InArgument<string>((env) => "Item added: " + Item.Get(env)) } }),
};

// Construct a workflow using StartOrder, ReplyToStartOrder, and AddItem.

В предыдущем примере показана корреляция на основе содержимого, инициализированная действием SendReply. Набор MessageQuerySet указывает, что в качестве данных, применяемых для идентификации последующих сообщений для этой службы, используется OrderId.

SendReply ReplyToStartOrder = new SendReply
{
    Request = StartOrder,
    Content = SendParametersContent.Create(new Dictionary<string, InArgument>
        { { "OrderId", new InArgument<string>((env) => OrderId.Get(env)) } }),
    CorrelationInitializers =
    {
        new QueryCorrelationInitializer
        {
            CorrelationHandle = OrderIdHandle,
            MessageQuerySet = new MessageQuerySet
            {
                {
                    "OrderId", 
                    new XPathMessageQuery("sm:body()/tempuri:StartOrderResponse/tempuri:OrderId")
                }
            }
        }
    }
};

Действие Receive, следующее за действием SendReply в рабочем процессе, соответствует корреляции, инициализированной действием SendReply. Оба действия используют один и тот же дескриптор CorrelationHandle, но для каждого из них используются отдельные MessageQuerySet и XPathMessageQuery, указывающие, где расположены идентифицирующие данные в определенном сообщении. Для действия, инициализирующего корреляцию, MessageQuerySet указывается в свойстве CorrelationInitializers, а для любого следующего действия Receive — в свойстве CorrelatesOn.

Receive AddItem = new Receive
{
    ServiceContractName = "IOrderService",
    OperationName = "AddItem",
    CorrelatesWith = OrderIdHandle,
    CorrelatesOn = new MessageQuerySet
    {
        {
            "OrderId", 
              new XPathMessageQuery("sm:body()/tempuri:AddItem/tempuri:OrderId")
        }
    },
    Content = ReceiveParametersContent.Create(new Dictionary<string, OutArgument>
        { { "OrderId", new OutArgument<string>(OrderId) },  
        { "Item", new OutArgument<string>(Item) } })
};

Корреляция на основе содержимого может инициализироваться любым действием по обмену сообщениями (Send, Receive, SendReply, ReceiveReply), если данные передаются в составе сообщения. Если определенные данные не являются частью сообщения, то корреляция может быть инициализирована явно с помощью действия InitializeCorrelation. Если для уникальной идентификации сообщения требуется несколько блоков данных, то в набор MessageQuerySet можно добавить несколько запросов. В этих примерах CorrelationHandle был явно указан для каждого действия с помощью свойств CorrelatesWith или CorrelationHandle, но, если для всего рабочего процесса требуется только одна корреляция, как в примере с корреляцией на основе OrderId, достаточно неявного управления дескриптором взаимосвязи с помощью WorkflowServiceHost.

Использование действия InitializeCorrelation

В предыдущем примере OrderId направлялся вызывающему объекту с помощью действия SendReply, которое служило для инициализации корреляции. Это поведение может выполняться с помощью действия InitializeCorrelation. Действие InitializeCorrelation использует дескриптор CorrelationHandle и словарь элементов, представляющий данные для сопоставления сообщения с правильным экземпляром. Чтобы использовать действие InitializeCorrelation, описанное в предыдущем образце, удалите CorrelationInitializers из действия SendReply и инициализируйте корреляцию с помощью действия InitializeCorrelation.

Variable<string> OrderId = new Variable<string>();
Variable<string> Item = new Variable<string>();
Variable<CorrelationHandle> OrderIdHandle = new Variable<CorrelationHandle>();

InitializeCorrelation OrderIdCorrelation = new InitializeCorrelation
{
    Correlation = OrderIdHandle,
    CorrelationData = { { "OrderId", new InArgument<string>(OrderId) } }
};

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

SendReply ReplyToStartOrder = new SendReply
{
    Request = StartOrder,
    Content = SendParametersContent.Create(new Dictionary<string, InArgument> { { "OrderId", new InArgument<string>((env) => OrderId.Get(env)) } }),
};

// Other messaging activities omitted...

Затем действие InitializeCorrelation используется в рабочем процессе, после того как заполняются переменные, содержащие данные, но перед действием Receive, связанным с инициализированным CorrelationHandle.

// Construct a workflow using OrderIdCorrelation, StartOrder, ReplyToStartOrder,
// and other messaging activities.
Activity wf = new Sequence
{
    Variables =
    {
        OrderId,
        Item,
        OrderIdHandle
    },
    Activities =
    {
        // Wait for a new order.
        StartOrder,
        // Assign a unique identifier to the order.
        new Assign<string>
        {
            To = new OutArgument<string>( (env) => OrderId.Get(env)),
            Value = new InArgument<string>( (env) => Guid.NewGuid().ToString() )
        },
        ReplyToStartOrder,
        // Initialize the correlation.
        OrderIdCorrelation,
        // Wait for an item to be added to the order.
        AddItem,
        ReplyToAddItem
     }
};

Настройка запросов XPath с помощью конструктора рабочих процессов

В предыдущих примерах действия и запросы XPath, используемые в запросах к сообщениям, были определены в коде. Конструктор рабочих процессов в среде Visual Studio 2010 также предоставляет возможность создавать выражения XPath из типов DataContract для корреляции на основе содержимого. Первый запрос Xpath, настроенный в предыдущем примере, был настроен для действия SendReply.

SendReply ReplyToStartOrder = new SendReply
{
    Request = StartOrder,
    Content = SendParametersContent.Create(new Dictionary<string, InArgument>
        { { "OrderId", new InArgument<string>((env) => OrderId.Get(env)) } }),
    CorrelationInitializers =
    {
        new QueryCorrelationInitializer
        {
            CorrelationHandle = OrderIdHandle,
            MessageQuerySet = new MessageQuerySet
            {
                {
                    "OrderId", 
                    new XPathMessageQuery("sm:body()/tempuri:StartOrderResponse/tempuri:OrderId")
                }
            }
        }
    }
};

Чтобы настроить выражение XPath для действия по обмену сообщениями в конструкторе рабочих процессов, выберите действие в конструкторе рабочих процессов. Если действие вызывает корреляцию, как в предыдущем примере, нажмите кнопку с многоточием рядом со свойством CorrelationInitializers в окне Свойства. Отобразится диалоговое окно Добавление инициализаторов корреляции. В этом диалоговом окне можно указать тип корреляции и выбрать содержимое, применяемое для корреляции. Переменная CorrelationHandle указывается в окне Добавить инициализатор, а тип корреляции и данные, используемые для корреляции, выбираются в разделе Запросы XPath диалогового окна.

Диалоговое окно CorrelationInitializer

Второй запрос XPath в предыдущем примере был настроен в действии Receive.

Receive AddItem = new Receive
{
    ServiceContractName = "IOrderService",
    OperationName = "AddItem",
    CorrelatesWith = OrderIdHandle,
    CorrelatesOn = new MessageQuerySet
    {
        {
            "OrderId", 
              new XPathMessageQuery("sm:body()/tempuri:AddItem/tempuri:OrderId")
        }
    },
    Content = ReceiveParametersContent.Create(new Dictionary<string, OutArgument>
        { { "OrderId", new OutArgument<string>(OrderId) },  
        { "Item", new OutArgument<string>(Item) } })
};

Чтобы настроить запрос XPath для действия по обмену сообщениями, не инициализирующего корреляцию, выберите действие в конструкторе рабочего процесса и нажмите кнопку с многоточием для свойства CorrelatesOn в окне Свойства. Отобразится диалоговое окно Определение CorrelatesOn.

Корреляция по определению

В этом диалоговом окне можно указать дескриптор CorrelationHandle и выбрать элементы в списке Запросы XPath, чтобы создать запрос XPath.