private void MoveToPoisonMessage(SqlTransaction transaction
                                         , ServiceBrokerMessage serviceBrokerMessage
                                         , IncomingTransportMessage transportMessage
                                         , Exception exception
                                         , string errorCode
                                         , int retries)
        {
            var origin_service_name = "";

            if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
            {
                origin_service_name = transportMessage.Headers[StandardHeaders.OriginatingAddress];
            }
            else if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.ReplyToAddress))
            {
                origin_service_name = transportMessage.Headers[StandardHeaders.ReplyToAddress];
            }
            else
            {
                origin_service_name = ServiceBrokerWrapper.TryGetConversationFarService(transaction, serviceBrokerMessage.ConversationHandle);
            }
            var enqueuedDateTime = DateTime.MinValue;

            if (transportMessage != null && transportMessage.Headers.ContainsKey(StandardHeaders.TimeSent))
            {
                DateTime.TryParse(transportMessage.Headers[StandardHeaders.TimeSent], out enqueuedDateTime);
            }
            try
            {
                PoisonMessageSqlHelper.WriteToPoisonMessageTable(
                    transaction
                    , serviceBrokerMessage.ConversationHandle
                    , origin_service_name
                    , enqueuedDateTime
                    , ServiceBrokerService
                    , ServiceBrokerQueue
                    , serviceBrokerMessage.Body
                    , MaxRetries
                    , errorCode
                    , exception);
            }
            catch (Exception e)
            {
                logger.Fatal(e, "Failed to write PoisonMessage for message {0} from queue {1}", serviceBrokerMessage.ConversationHandle, ServiceBrokerQueue);
                // suppress -- don't let this exception take down the process
            }
            PoisonMessageDetected?.Invoke(this, new PoisonMessageDetectedEventArgs()
            {
                MessageId         = serviceBrokerMessage.ConversationHandle.ToString(),
                OriginServiceName = origin_service_name,
                ServiceName       = this.ServiceBrokerService,
                QueueName         = this.ServiceBrokerQueue,
                MessageBody       = serviceBrokerMessage.Body,
                Retries           = retries,
                ErrorCode         = errorCode,
                Exception         = exception,
            });
        }
        private void TryReceiveMessage()
        {
            // NOTE this method _should not_ throw an exception!
            ServiceBrokerMessage message     = null;
            SqlTransaction       transaction = null;

            try
            {
                transaction = SqlServerTransactionManager.BeginTransaction(ConnectionString);
                logger.Debug("RECEIVE FROM {0}", ServiceBrokerQueue);
                message = ServiceBrokerWrapper.WaitAndReceive(transaction, ServiceBrokerQueue, waitTimeout);
            }
            catch (Exception e)
            {
                logger.Error(e, "Exception caught trying to receive from queue {0}. Rolling back and backing off before reconnecting.", ServiceBrokerQueue);
                SqlServerTransactionManager.TryRollbackTransaction(transaction);
                SqlServerTransactionManager.TryForceDisposeTransactionAndConnection(transaction);
                Thread.Sleep(1000);          // back-off because we will return and loop immediately
                workerThreadPool.Release(1); // always release before returning!
                return;
            }

            // No message? That's okay
            if (message == null)
            {
                try
                {
                    SqlServerTransactionManager.CommitTransactionAndDisposeConnection(transaction);
                }
                catch (Exception)
                {
                    SqlServerTransactionManager.TryForceDisposeTransactionAndConnection(transaction);
                    throw;
                }
                workerThreadPool.Release(1); // always release before returning!
                return;
            }

            // start a
            Task.Run(() =>
            {
                // TryHandleHessage _should not_ throw an exception. It will also commit and cleanup the transactions
                try
                {
                    TryHandleMessage(transaction, message);
                }
                finally
                {
                    var previousCount = workerThreadPool.Release(1); // release this semaphore back to the pool regardless of what happened
                    logger.Debug("Current Concurrent Requests = {0}", MaxConcurrency - previousCount);
                }
            });
        }
 private void InitServiceBroker()
 {
     using (var connection = SqlServerTransactionManager.OpenConnection(ConnectionString))
         using (var transaction = connection.BeginTransaction())
         {
             // Ensure the service and queue exist
             ServiceBrokerWrapper.CreateServiceAndQueue(transaction, ServiceBrokerService, ServiceBrokerQueue, ServiceBrokerMessageType, ServiceBrokerContract);
             transaction.Commit();
             connection.Close();
         }
     PoisonMessageSqlHelper.EnsureTableExists(ConnectionString);
 }
        private void TryHandleMessageInner(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            // Only handle transport messages
            if (message.MessageTypeName != ServiceBrokerMessageType &&
                !message.IsDialogTimerMessage())
            {
                logger.Debug("Ignoring message of type {0} from queue {1}", message.MessageTypeName, ServiceBrokerQueue);
                return; // ignore
            }
            IncomingTransportMessage transportMessage = null;

            try
            {
                if (message.IsDialogTimerMessage())
                {
                    //transportMessage = TryLoadDialogTimerTimeoutMessage(transaction, message);
                    throw new NotImplementedException();
                }
                else
                {
                    transportMessage = TryDeserializeTransportMessage(message);
                }

                if (!transportMessage.Headers.ContainsKey(StandardHeaders.OriginatingAddress))
                {
                    transportMessage.Headers[StandardHeaders.OriginatingAddress] = ServiceBrokerWrapper.TryGetConversationFarService(transaction, message.ConversationHandle);
                }

                Exception lastException = null;
                if (faultManager.HasMaxRetriesExceeded(transportMessage, out lastException))
                {
                    logger.Debug("MaxRetriesExceeded: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                    MoveToPoisonMessage(transaction, message, transportMessage, lastException, "MaxRetriesExceeded", MaxRetries);
                    return; // return without error to commit transaction
                }
                logger.Debug("Notifying observers of new TransportMessage for message {0} from queue {1}.", message.ConversationHandle, ServiceBrokerQueue);
                this.MessageAvailable?.Invoke(this, new MessageAvailableEventArgs(transportMessage));
            }
            catch (CannotDeserializeMessageException e)
            {
                // If we can't deserialize the transport message or inner message, there is no reason to retry.
                // note: error is already logged so we don't need to log here
                logger.Debug("DeserializeError: Moving message {0} from queue {1} to poison message table.", message.ConversationHandle.ToString(), ServiceBrokerQueue);
                MoveToPoisonMessage(transaction, message, transportMessage, e, "DeserializeError", 0);
                return; // return without error to commit transaction
            }
        }
        private void TryHandleMessage(SqlTransaction transaction, ServiceBrokerMessage message)
        {
            logger.Debug("Received message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
            // NOTE this method _should not_ throw an exception!
            // It is responsible for committing or rolling back the SqlTransaction
            // that was passed in.
            // We can keep track of the openTransaction on the current thread in case we need it.
            SqlServerTransactionManager.SaveTransactionForCurrentTask(transaction);
            try
            {
                // this may throw an exception if we need to rollback
                TryHandleMessageInner(transaction, message);
                // End the conversation and commit
                ServiceBrokerWrapper.EndConversation(transaction, message.ConversationHandle);
                SqlServerTransactionManager.CommitTransactionAndDisposeConnection(transaction);
                faultManager.ClearFailuresForMessage(message.ConversationHandle.ToString());
                logger.Debug("Committed message {0} from queue {1}", message.ConversationHandle, ServiceBrokerQueue);
            }
            catch (Exception e)
            {
                SqlServerTransactionManager.TryRollbackTransaction(transaction);
                faultManager.IncrementFailuresForMessage(message.ConversationHandle.ToString(), e);
                logger.Error(e, "Exception caught handling message {0} from queue {1}. Rolling back transaction to retry.", message.ConversationHandle, ServiceBrokerQueue);
#if DEBUG
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    // System.Diagnostics.Debugger.Break();
                }
#endif
            }
            finally
            {
                SqlServerTransactionManager.ClearTransactionForCurrentTask();
                SqlServerTransactionManager.TryForceDisposeTransactionAndConnection(transaction); // shoud not throw exception
            }
        }
        public void Send(OutgoingTransportMessage transportMessage, IEnumerable <string> addresses)
        {
            var transaction = SqlServerTransactionManager.TryGetTransactionForCurrentTask();
            var commitAndDisposeTransaction = false;

            if (transaction == null)
            {
                transaction = SqlServerTransactionManager.BeginTransaction(ConnectionString);
                commitAndDisposeTransaction = true;
            }
            try
            {
                transportMessage.Headers[StandardHeaders.TimeSent] = DateTime.UtcNow.ToString("o");
                if (!transportMessage.Headers.ContainsKey(StandardHeaders.ReplyToAddress))
                {
                    transportMessage.Headers[StandardHeaders.ReplyToAddress] = this.ServiceBrokerSendingService;
                }
                transportMessage.Headers[StandardHeaders.OriginatingAddress]   = this.ServiceBrokerSendingService;
                transportMessage.Headers[StandardHeaders.ContentType]          = messageEncoder.ContentType;
                transportMessage.Headers[StandardHeaders.EnclosedMessageTypes] = string.Join(",", messageMapper.GetEnclosedMessageTypes(transportMessage.Message.GetType()).Distinct());

                using (var stream = new MemoryStream())
                {
                    var encodedMessage = messageEncoder.EncodeAsString(transportMessage.Message);
                    FastXmlTransportMessageSerializer.Serialize(transportMessage.Headers, encodedMessage, stream);
                    var messageBuffer = stream.ToArray();
                    foreach (var destination in addresses)
                    {
                        var conversationHandle = ServiceBrokerWrapper.SendOne(
                            transaction: transaction
                            , initiatorServiceName: ServiceBrokerSendingService
                            , targetServiceName: destination
                            , messageContractName: ServiceBrokerContract
                            , messageType: ServiceBrokerMessageType
                            , body: messageBuffer);
#if DEBUG
                        logger.Debug(string.Format("Sending message {0} with Handle {1} to Service Named {2}.",
                                                   transportMessage.Message.GetType().AssemblyQualifiedName,
                                                   conversationHandle,
                                                   destination));
                        logger.Debug(string.Format("ToString() of the message yields: {0}\n" +
                                                   "Message headers:\n{1}",
                                                   transportMessage.Message.ToString(),
                                                   string.Join(", ", transportMessage.Headers.Select(h => h.Key + ":" + h.Value).ToArray())));
#endif
                    }
                }

                if (commitAndDisposeTransaction)
                {
                    SqlServerTransactionManager.CommitTransactionAndDisposeConnection(transaction);
                }
            }
            catch (Exception)
            {
                if (commitAndDisposeTransaction)
                {
                    SqlServerTransactionManager.TryForceDisposeTransactionAndConnection(transaction);
                }
                throw;
            }
        }