Недавно мне прислали вопрос о том, как обновить две таблицы в БД Oracle в одной транзакции, отправив два соответствующих сообщения из BizTalk через WCF Oracle адаптер.
Казалось бы, интутитивно, заключение двух Send форм в Atomic transaction scope должно привести к желаемому результату. Однако, такие транзакции выполняются на уровне MessageBox.
Как всегда, решение имеет несколько вариантов. Стандартные варианты в таких случаях - иметь хранимую процедуру, выполняющую обновление двух таблиц и вызывать ее через адаптер, либо воспользовать Expression формой, где через средства .NET выполнить обновление в транзакции.
С новым же WCF Oracle адаптером, у вас появилась возможность поддержки транзакций для серии отдельных сообщений. По этой ссылке можно прочитать про эту возможность. Далее я опишу, как это сделать своими словами.
Итак, у вас есть набор сообщений для Oracle, которые вы хотите отправить в единой транзакции. Первое, что нужно сделать это установить у таких сообщений следующие свойства:
Это стандартное свойство, которое устанавливается адаптером в идентификатор исходящего порта (в нашем случае Oracle порта). Все сообщения в транзакции должны иметь единое значение этого свойства. Т.е. все такие сообщения должны отправляться через единый исходящий порт (возможно с различными операциями на каждое действие).
InterchangeID так же стандартное свойство и оно позволяет отнести сообщения с единым значением InterchangeID к единой сессии передачи сообщений (например, при дебатчинге входящего сообщения, все получившиеся сообщения имеют одинаковое значение InterchangeID). Итак, вам придется установить это свойство в оркестровке для серии транзакционных сообщений в единое значение - это может быть просто сгенерированный Guid или взятый InterchangeID из входящего сообщения.
Третье свойство определено в сборке Microsoft.Adapters.OracleDB.BiztalkPropertySchema.dll, которая идет в поставке с BizTalk Adapter Pack (находится в папке bin, где установлен пакет). Добавьте эту сборку, как ресурс через административную консоль BizTalk (например, в стандартное приложение BizTalk Application 1). В ваш проект же добавьте на нее ссылку (через Add Reference).
TransactionState - это очень важное свойство, т.к. оно регулирует действия над транзакцией, и может принимать следующие значения:
Контрольное сообщение имеет следующий вид: <Oracle_Transaction xmlns="http://Microsoft.LobServices.OracleDB/2007/03/Oracle_Transaction"></Oracle_Transaction>. Операция отправляющая контрольное сообщение в адаптер должна мэпиться на SOAP action "http://Microsoft.LobServices.OracleDB/2007/03/Oracle_Transaction".
Схема контрольного сообщения (дабы не создавать его руками) находится в папке Schemas, где установлен BizTalk Adapter Pack.
Это все о свойствах.
Сам WCF-custom адаптер с байндингом oracleDBBinding должен иметь свойство enableBizTalkCompatibilityMode установленным в true (по умолчанию, это так).
Небольшой пример. Я получаю входящее сообщение, создаю два исходящих, каждое из которых обновляет различные таблицы Oracle и затем завершаю транзакцию.
Моя оркестровка выглядит так:
В форме MessageAssignment происходит самое интересное: я устанавливаю свойства всей серии Oracle сообщений:
doc = new System.Xml.XmlDocument();doc.LoadXml("<ns0:Oracle_Transaction xmlns:ns0='http://Microsoft.LobServices.OracleDB/2007/03/Oracle_Transaction'></ns0:Oracle_Transaction>");
mCommit = doc;
mInsertProduct(Microsoft.Adapters.OracleDB.BiztalkPropertySchema.TransactionState) = "BEGIN";mInsertProduct(BTS.InterchangeID) = mInbound(BTS.InterchangeID);mInsertPO(Microsoft.Adapters.OracleDB.BiztalkPropertySchema.TransactionState) = "REUSE";mInsertPO(BTS.InterchangeID) = mInbound(BTS.InterchangeID);mCommit(Microsoft.Adapters.OracleDB.BiztalkPropertySchema.TransactionState) = "COMMIT";mCommit(BTS.InterchangeID) = mInbound(BTS.InterchangeID);
Последовательность отправки сообщений: mInsertProduct, mInsertPO, mCommit.
Порт Oracle в консоли администратора имеет следующие настройки WCF-Custom для SOAP Action Header:
<BtsActionMapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Operation Name="InsertProduct" Action="http://Microsoft.LobServices.OracleDB/2007/03/SUPPLIER/Table/PRODUCT/Insert" /> <Operation Name="InsertPO" Action="http://Microsoft.LobServices.OracleDB/2007/03/SUPPLIER/Table/PURCHASEORDER/Insert" /> <Operation Name="ControlTransaction" Action="http://Microsoft.LobServices.OracleDB/2007/03/Oracle_Transaction" /></BtsActionMapping>
Вот пожалуй и все. Теперь вставка в две разные таблицы будет производится в единой транзакции.
Стас