public void SendAsync <T>(T methodObj, object methodState, Action <IMessageSentResult <T> > methodResultCallBack, IServiceBusSerializer methodSerializer = null, IDictionary <string, object> methodMetadata = null) { Guard.ArgumentNotNull(methodObj, "obj"); Guard.ArgumentNotNull(methodResultCallBack, "resultCallBack"); methodSerializer = methodSerializer ?? configuration.DefaultSerializer.Create(); var sw = new Stopwatch(); sw.Start(); Action <T, object, Action <IMessageSentResult <T> >, IServiceBusSerializer, IDictionary <string, object> > sendAction = null; sendAction = ((obj, state, resultCallBack, serializer, metadata) => { IBrokeredMessage message = null; Exception failureException = null; bool resultSent = false; //I am not able to determine when the exception block is called. // Use a retry policy to execute the Send action in an asynchronous and reliable fashion. retryPolicy.ExecuteAction ( (cb) => { failureException = null; //we may retry so we must null out the error. try { // A new BrokeredMessage instance must be created each time we send it. Reusing the original BrokeredMessage instance may not // work as the state of its BodyStream cannot be guaranteed to be readable from the beginning. message = configurationFactory.MessageFactory.CreateBrokeredMessage(serializer.Serialize(obj)); message.MessageId = Guid.NewGuid().ToString(); message.Properties.Add(TYPE_HEADER_NAME, obj.GetType().FullName.Replace('.', '_')); if (metadata != null) { foreach (var item in metadata) { message.Properties.Add(item.Key, item.Value); } } logger.Debug("sendAction BeginSend Type={0} Serializer={1} MessageId={2}", obj.GetType().FullName, serializer.GetType().FullName, message.MessageId); // Send the event asynchronously. defaultClient.BeginSend(message, cb, null); } catch (Exception ex) { failureException = ex; throw; } }, (ar) => { try { failureException = null; //we may retry so we must null out the error. // Complete the asynchronous operation. This may throw an exception that will be handled internally by the retry policy. logger.Debug("sendAction EndSend Begin Type={0} Serializer={1} MessageId={2}", obj.GetType().FullName, serializer.GetType().FullName, message.MessageId); defaultClient.EndSend(ar); logger.Debug("sendAction EndSend End Type={0} Serializer={1} MessageId={2}", obj.GetType().FullName, serializer.GetType().FullName, message.MessageId); } catch (Exception ex) { failureException = ex; throw; } }, () => { // Ensure that any resources allocated by a BrokeredMessage instance are released. if (message != null) { message.Dispose(); message = null; } if (serializer != null) { serializer.Dispose(); serializer = null; } sw.Stop(); if (!resultSent) { resultSent = true; ExtensionMethods.ExecuteAndReturn(() => resultCallBack(new MessageSentResult <T>() { IsSuccess = failureException == null, State = state, ThrownException = failureException, TimeSpent = sw.Elapsed })); } }, (ex) => { // Always dispose the BrokeredMessage instance even if the send operation has completed unsuccessfully. if (message != null) { message.Dispose(); message = null; } if (serializer != null) { serializer.Dispose(); serializer = null; } failureException = ex; // Always log exceptions. logger.Error <Exception>("Send failed {0}", ex); sw.Stop(); //see if the reason is the topic was deleted and if it was, create the topic and set a new error since there will be no receivers. var topicException = ex as MessagingEntityNotFoundException; if (topicException != null && topicException.Detail != null && topicException.Detail.Message.IndexOf("40400") > -1) { logger.Info("Topic was deleted. Attempting to Recreate."); EnsureTopic(this.configuration.TopicName, this.configuration.EnablePartitioning); failureException = new TopicDeletedException(); logger.Info("Topic was deleted. Recreate Complete."); } if (!resultSent) { resultSent = true; ExtensionMethods.ExecuteAndReturn(() => resultCallBack(new MessageSentResult <T>() { IsSuccess = failureException == null, State = state, ThrownException = failureException, TimeSpent = sw.Elapsed })); } } ); //asyc }); //action sendAction(methodObj, methodState, methodResultCallBack, methodSerializer, methodMetadata); }
private void ProcessMessagesForSubscriptionLegacy() { try { logger.Info("ProcessMessagesForSubscription Message Start {0} Declared {1} MessageTytpe {2}, IsReusable {3}", data.EndPointData.SubscriptionName, data.EndPointData.DeclaredType.ToString(), data.EndPointData.MessageType.ToString(), data.EndPointData.IsReusable); var gt = typeof(IReceivedMessage <>).MakeGenericType(data.EndPointData.MessageType); var methodInfo = data.EndPointData.DeclaredType.GetMethod("Handle", new Type[] { gt }); var waitTimeout = TimeSpan.FromSeconds(30); // Declare an action acting as a callback whenever a message arrives on a queue. AsyncCallback completeReceive = null; // Declare an action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. Action <Exception> recoverReceive = null; // Declare an action implementing the main processing logic for received messages. Action <AzureReceiveState> processMessage = ((receiveState) => { // Put your custom processing logic here. DO NOT swallow any exceptions. ProcessMessageCallBack(receiveState); }); bool messageReceived = false; bool lastAttemptWasError = false; // Declare an action responsible for the core operations in the message receive loop. Action receiveMessage = (() => { // Use a retry policy to execute the Receive action in an asynchronous and reliable fashion. retryPolicy.ExecuteAction ( (cb) => { if (lastAttemptWasError) { if (data.EndPointData.AttributeData.PauseTimeIfErrorWasThrown > 0) { Thread.Sleep(data.EndPointData.AttributeData.PauseTimeIfErrorWasThrown); } else { Thread.Sleep(1000); } } // Start receiving a new message asynchronously. data.Client.BeginReceive(waitTimeout, cb, null); }, (ar) => { messageReceived = false; // Make sure we are not told to stop receiving while we were waiting for a new message. if (!data.CancelToken.IsCancellationRequested) { IBrokeredMessage msg = null; bool isError = false; try { // Complete the asynchronous operation. This may throw an exception that will be handled internally by retry policy. msg = data.Client.EndReceive(ar); // Check if we actually received any messages. if (msg != null) { // Make sure we are not told to stop receiving while we were waiting for a new message. if (!data.CancelToken.IsCancellationRequested) { // Process the received message. messageReceived = true; logger.Debug("ProcessMessagesForSubscription Start received new message: {0}", data.EndPointData.SubscriptionNameDebug); var receiveState = new AzureReceiveState(data, methodInfo, serializer, msg); processMessage(receiveState); logger.Debug("ProcessMessagesForSubscription End received new message: {0}", data.EndPointData.SubscriptionNameDebug); } } } catch (Exception) { isError = true; throw; } finally { // With PeekLock mode, we should mark the failed message as abandoned. if (msg != null) { if (data.Client.Mode == ReceiveMode.PeekLock) { if (isError) { // Abandons a brokered message. This will cause Service Bus to unlock the message and make it available // to be received again, either by the same consumer or by another completing consumer. SafeAbandon(msg); } else { // Mark brokered message as completed at which point it's removed from the queue. SafeComplete(msg); } } msg.Dispose(); } } } // Invoke a custom callback method to indicate that we have completed an iteration in the message receive loop. completeReceive(ar); }, () => { //do nothing, we completed. }, (ex) => { // Invoke a custom action to indicate that we have encountered an exception and // need further decision as to whether to continue receiving messages. recoverReceive(ex); }); }); // Initialize a custom action acting as a callback whenever a message arrives on a queue. completeReceive = ((ar) => { lastAttemptWasError = false; if (!data.CancelToken.IsCancellationRequested) { // Continue receiving and processing new messages until we are told to stop. receiveMessage(); } else { data.SetMessageLoopCompleted(); } if (messageReceived) { logger.Debug("ProcessMessagesForSubscription Message Complete={0} Declared={1} MessageTytpe={2} IsReusable={3}", data.EndPointData.SubscriptionName, data.EndPointData.DeclaredType.ToString(), data.EndPointData.MessageType.ToString(), data.EndPointData.IsReusable); } }); // Initialize a custom action acting as a callback whenever a non-transient exception occurs while receiving or processing messages. recoverReceive = ((ex) => { // Just log an exception. Do not allow an unhandled exception to terminate the message receive loop abnormally. lastAttemptWasError = true; if (!data.CancelToken.IsCancellationRequested && typeof(ThreadAbortException) != ex.GetType()) { //The subscription may have been deleted. If it was, then we want to recreate it. var subException = ex as MessagingEntityNotFoundException; if (subException != null && subException.Detail != null && subException.Detail.Message.IndexOf("40400") > -1) { logger.Info("Subscription was deleted. Attempting to Recreate."); Configure(endpoint); logger.Info("Subscription was deleted. Recreated."); } else { logger.Error(string.Format("ProcessMessagesForSubscription Message Error={0} Declared={1} MessageTytpe={2} IsReusable={3} Error={4}", data.EndPointData.SubscriptionName, data.EndPointData.DeclaredType.ToString(), data.EndPointData.MessageType.ToString(), data.EndPointData.IsReusable, ex.ToString())); } // Continue receiving and processing new messages until we are told to stop regardless of any exceptions. receiveMessage(); } else { data.SetMessageLoopCompleted(); } if (messageReceived) { logger.Debug("ProcessMessagesForSubscription Message Complete={0} Declared={1} MessageTytpe={2} IsReusable={3}", data.EndPointData.SubscriptionName, data.EndPointData.DeclaredType.ToString(), data.EndPointData.MessageType.ToString(), data.EndPointData.IsReusable); } }); // Start receiving messages asynchronously. receiveMessage(); } catch (ThreadAbortException) { //do nothing, let it exit } catch (Exception ex) { failCounter++; logger.Error("ProcessMessagesForSubscription: Error during receive during loop. See details and fix: {0}", ex); if (failCounter < 100) { //try again ProcessMessagesForSubscription(); } } }