На основе содержимого
Данный раздел относится к версии Windows Workflow Foundation 4.
Когда службы рабочего процесса обмениваются данными с клиентами и другими службами, в сообщениях часто встречаются данные, уникальным образом направляющие сообщение в определенный экземпляр. При корреляции на основе содержимого эти данные в сообщении, например номер заказчика или идентификатор заказа, используются для маршрутизации сообщения в определенный экземпляр рабочего процесса. В этом разделе описывается использование корреляции на основе содержимого в рабочих процессах.
Использование корреляции на основе содержимого
Корреляция на основе содержимого применяется, если служба рабочего процесса имеет несколько методов, доступных одному клиенту, и некоторые данные в сообщениях идентифицируют нужный экземпляр.
Примечание |
---|
Корреляция на основе содержимого полезна, если нельзя применить корреляцию на основе контекста, так как привязка не относится ни к одному поддерживаемому типу привязок для обмена контекстом. Дополнительные сведения корреляции на основе контекста см. в разделе Обмен контекстом. |
В каждом действии по обмену сообщениями при обмене данными должно быть указано расположение в сообщении данных, уникальным образом идентифицирующих экземпляр. Это делается путем указания набора MessageQuerySet с помощью QueryCorrelationInitializer или CorrelatesOn, выбирающих из сообщения данные, уникальным образом идентифицирующие экземпляр.
Внимание! |
---|
Данные, используемые для идентификации экземпляра, хэшируются в ключ корреляции. Необходимо убедиться, что данные, используемые для корреляции, являются уникальными. В противном случае в хэшированном ключе могут возникнуть конфликты, которые приведут к неправильной маршрутизации сообщения. Например, корреляция на основе только имени заказчика может вызвать конфликт, так как могут иметься заказчики с одинаковыми именами. Двоеточие (:) не должно встречаться в данных, используемых для корреляции сообщений, так как оно уже применяется в качестве разделителя ключа и значения в запросе к сообщению, формирующих строку, которая впоследствии хэшируется. |
В следующем примере начальное действие 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 диалогового окна.
Второй запрос 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.