public void ProcessBatch() { bool needToLeave = false; busy = true; try { // used to block the Terminate from BizTalk if (!this.control.Enter()) { needToLeave = false; return; } needToLeave = true; StateSettings stateSettings = new StateSettings(); stateSettings.FindFirst = this.properties.FirdFirst; stateSettings.WorkingFeed = this.properties.FirstFeed; stateSettings.Id = atomState.LastEntryId; AtomReader atom = new AtomReader(this.properties.Uri, stateSettings, this.properties.SecuritySettings, this.properties.FeedMax); Feed feed = null; bool discard = atom.IdFound; string stateId = atomState.LastEntryId; string lastId = String.Empty; while ((feed = atom.NextFeed()) != null && feed.Entries.Count > 0) { ManualResetEvent orderedEvent = null; CommittableTransaction transaction = null; //using (SyncReceiveSubmitBatch batch = new SyncReceiveSubmitBatch(this.transportProxy, this.control, 1)) Entry entry = feed.Entries.PopOrNUll(); while (entry != null) { if (discard == false) { orderedEvent = new ManualResetEvent(false); transaction = new CommittableTransaction(); atomState.LastEntryId = entry.Id; atomState.LastUpdated = feed.Updated; atomState.LastFeed = feed.Uri; SaveState(transaction); using (SingleMessageReceiveTxnBatch batch = new SingleMessageReceiveTxnBatch(this.transportProxy, this.control, transaction, orderedEvent)) { batch.SubmitMessage(CreateMessage(entry)); batch.Done(); orderedEvent.WaitOne(); } } if (stateId == entry.Id) { discard = false; } entry = feed.Entries.PopOrNUll(); } } // no exception in Done so we will be getting a BatchComplete which will do the necessary Leave needToLeave = false; } catch (MaxDeepthException deepth) { this.transportProxy.ReceiverShuttingdown(this.properties.Uri, deepth); } catch (InvalidConfiguration arg) { this.transportProxy.ReceiverShuttingdown(this.properties.Uri, arg); } catch (WebException ex) { this.transportProxy.ReceiverShuttingdown(this.properties.Uri, ex); } catch (Exception e) { this.transportProxy.SetErrorInfo(e); } finally { busy = false; // if this is true there must have been some exception in or before Done if (needToLeave) { this.control.Leave(); } } }
public void SubmitBatch() { bool needToLeave = false; CommittableTransaction transaction = null; try { // used to block the Terminate from BizTalk if (!this.control.Enter()) { needToLeave = false; return; } needToLeave = true; ManualResetEvent orderedEvent = new ManualResetEvent(false); string connectionString = this.properties.ConnectionString; string cmdText = this.properties.CmdText; string rootElementName = "Root"; MemoryStream stream = new MemoryStream(); bool dataAvailable = false; // Create the System.Transactions transaction transaction = new CommittableTransaction(); // Explicit interop with COM+ - this should not be necessary in future (on SQL 2005 for example) using (TransactionScope ts = new TransactionScope(transaction, TimeSpan.FromHours(1), EnterpriseServicesInteropOption.Full)) { // The connection is created inside the TransactionScope so the database will be included in the transaction using (SqlConnection connection = new SqlConnection(connectionString)) { connection.Open(); SqlCommand command = new SqlCommand(cmdText, connection); // The Encoding on the writer should be consistent with the BizTalk message. // If you change this to UTF8 then you should change the BizTalk message CharSet to UTF8. XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = Encoding.Unicode; settings.Indent = true; settings.OmitXmlDeclaration = false; using (XmlWriter writer = XmlTextWriter.Create(stream, settings)) { writer.WriteStartDocument(); // Add the root element because the reader form the database doesn't // include the root element for the xml - it's just rows writer.WriteStartElement(rootElementName); // WriteNode is a little opaque checking the size of the underlying stream // is a simple way to see whether any data was retrieved. writer.Flush(); long beforeSize = stream.Length; using (XmlReader reader = command.ExecuteXmlReader()) { while (!reader.EOF) { writer.WriteNode(reader, true); } } writer.Flush(); long afterSize = stream.Length; // we only want to add a new message to BizTalk if the reader actually returned anything dataAvailable = (afterSize > beforeSize); writer.WriteEndElement(); } } // An exception will have skipped this next line ts.Complete(); } // Typically when polling like this you would want to loop checking dataAvailable and only // return to the sleep state when there is no data to feed into BizTalk. // // For simplicity this is not shown here. If this is written like this, consideration should also be // given to the .NET thread pool. Sitting on a .NET thread pool thread for an extended period can // result in scale out problems. It is advisable to build some type of yield into the loop, for example // having the worker function BeginInvoke itself if there is dataAvailable might work better than // a simple while loop. if (dataAvailable) { // Remember to Seek the stream or we won't get the data stream.Seek(0, SeekOrigin.Begin); // Note the batch has been given the CommittableTransaction and it will take responsibility for Commit using (Batch batch = new SingleMessageReceiveTxnBatch(this.transportProxy, this.control, transaction, orderedEvent)) { batch.SubmitMessage(CreateMessage(stream)); batch.Done(); } orderedEvent.WaitOne(); } else { // No data so no batch but in a database scenario (like this) we might still wish to Commit. // Other transactional adapters might choose to Rollback in this circumstance. transaction.Commit(); } // no exception in Done so we will be getting a BatchComplete which will do the necessary Leave needToLeave = false; } catch (Exception e) { this.transportProxy.SetErrorInfo(e); if (transaction != null) { transaction.Rollback(); } } finally { // if this is true there must have been some exception in or before Done if (needToLeave) { this.control.Leave(); } } }