public void CanRemoveHandlerThatHasBeenAdded() { var processor = new ServiceBusSessionProcessor( GetMockedConnection(), "entityPath", new ServiceBusPlugin[] { }, new ServiceBusSessionProcessorOptions()); Func <ProcessSessionMessageEventArgs, Task> eventHandler = eventArgs => Task.CompletedTask; Func <ProcessErrorEventArgs, Task> errorHandler = eventArgs => Task.CompletedTask; Func <ProcessSessionEventArgs, Task> sessionInitHandler = eventArgs => Task.CompletedTask; Func <ProcessSessionEventArgs, Task> sessionCloseHandler = eventArgs => Task.CompletedTask; processor.ProcessMessageAsync += eventHandler; processor.ProcessErrorAsync += errorHandler; processor.SessionInitializingAsync += sessionInitHandler; processor.SessionClosingAsync += sessionCloseHandler; Assert.That(() => processor.ProcessMessageAsync -= eventHandler, Throws.Nothing); Assert.That(() => processor.ProcessErrorAsync -= errorHandler, Throws.Nothing); Assert.That(() => processor.SessionInitializingAsync -= sessionInitHandler, Throws.Nothing); Assert.That(() => processor.SessionClosingAsync -= sessionCloseHandler, Throws.Nothing); // Assert that handlers can be added again. Assert.That(() => processor.ProcessMessageAsync += eventHandler, Throws.Nothing); Assert.That(() => processor.ProcessErrorAsync += errorHandler, Throws.Nothing); Assert.That(() => processor.SessionInitializingAsync += sessionInitHandler, Throws.Nothing); Assert.That(() => processor.SessionClosingAsync += sessionCloseHandler, Throws.Nothing); }
public void CannotRemoveHandlerThatHasNotBeenAdded() { var processor = new ServiceBusSessionProcessor( GetMockedConnection(), "entityPath", new ServiceBusPlugin[] { }, new ServiceBusSessionProcessorOptions()); // First scenario: no handler has been set. Assert.That(() => processor.ProcessMessageAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.ProcessErrorAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.SessionInitializingAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.SessionClosingAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); // Second scenario: there is a handler set, but it's not the one we are trying to remove. processor.ProcessMessageAsync += eventArgs => Task.CompletedTask; processor.ProcessErrorAsync += eventArgs => Task.CompletedTask; processor.SessionInitializingAsync += eventArgs => Task.CompletedTask; processor.SessionClosingAsync += eventArgs => Task.CompletedTask; Assert.That(() => processor.ProcessMessageAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.ProcessErrorAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.SessionInitializingAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); Assert.That(() => processor.SessionClosingAsync -= eventArgs => Task.CompletedTask, Throws.InstanceOf <ArgumentException>()); }
public async Task SessionProcessorActivities() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { using var listener = new TestDiagnosticListener(EntityScopeFactory.DiagnosticNamespace); var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); var messageCt = 2; var msgs = GetMessages(messageCt, "sessionId"); await sender.SendMessagesAsync(msgs); Activity[] sendActivities = AssertSendActivities(false, sender, msgs, listener); ServiceBusSessionProcessor processor = client.CreateSessionProcessor(scope.QueueName, new ServiceBusSessionProcessorOptions { AutoComplete = false, MaxReceiveWaitTime = TimeSpan.FromSeconds(10), MaxConcurrentSessions = 1 }); TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(); int processedMsgCt = 0; processor.ProcessMessageAsync += args => { processedMsgCt++; if (processedMsgCt == messageCt) { tcs.SetResult(true); } return(Task.CompletedTask); }; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); for (int i = 0; i < messageCt; i++) { (string Key, object Value, DiagnosticListener)receiveStart = listener.Events.Dequeue(); (string Key, object Value, DiagnosticListener)receiveStop = listener.Events.Dequeue(); (string Key, object Value, DiagnosticListener)processStart = listener.Events.Dequeue(); Assert.AreEqual(DiagnosticProperty.ProcessSessionMessageActivityName + ".Start", processStart.Key); Activity processActivity = (Activity)processStart.Value; AssertCommonTags(processActivity, processor.EntityPath, processor.FullyQualifiedNamespace); CollectionAssert.Contains( processActivity.Tags, new KeyValuePair <string, string>( DiagnosticProperty.MessageIdAttribute, msgs[i].MessageId)); CollectionAssert.Contains( processActivity.Tags, new KeyValuePair <string, string>( DiagnosticProperty.SessionIdAttribute, msgs[i].SessionId)); (string Key, object Value, DiagnosticListener)processStop = listener.Events.Dequeue(); Assert.AreEqual(DiagnosticProperty.ProcessSessionMessageActivityName + ".Stop", processStop.Key); } }; }
public CustomSessionMessageProcessor( ServiceBusSessionProcessor sessionProcessor, ILogger logger) : base(sessionProcessor) { _logger = logger; }
static async Task ReceiveSessionMessageAsync() { Console.WriteLine("========================================================="); Console.WriteLine("Press ENTER key to exit after receiving all the messages."); Console.WriteLine("========================================================="); var options = new ServiceBusSessionProcessorOptions { AutoCompleteMessages = false, MaxConcurrentSessions = 2, MaxConcurrentCallsPerSession = 2 }; await using ServiceBusSessionProcessor processor = string.IsNullOrWhiteSpace(SubscriptionName) ? srv.CreateSessionProcessor(QueueOrTopicName, options) : srv.CreateSessionProcessor(QueueOrTopicName, SubscriptionName, options); processor.ProcessMessageAsync += async(arg) => { Console.WriteLine($"Received message: {arg.SessionId} SequenceNumber: {arg.Message.SequenceNumber} Body: {Encoding.UTF8.GetString(arg.Message.Body)}"); if (AbandonarMultiplosDe > 0 && arg.Message.SequenceNumber % AbandonarMultiplosDe == 0) { await arg.AbandonMessageAsync(arg.Message); } else { await arg.CompleteMessageAsync(arg.Message); } }; processor.ProcessErrorAsync += ExceptionReceivedHandler; await processor.StartProcessingAsync(); Console.Read(); Console.WriteLine("Exit ..."); await processor.CloseAsync(); }
public Task StartProcessingAsync(CancellationToken cancellationToken = default) { return(_serviceBusProcessorAsObject switch { ServiceBusProcessor serviceBusProcessor => serviceBusProcessor.StartProcessingAsync(cancellationToken), ServiceBusSessionProcessor serviceBusSessionProcessor => serviceBusSessionProcessor.StartProcessingAsync(cancellationToken), _ => Task.CompletedTask });
public void MustSetMessageHandler() { var processor = new ServiceBusSessionProcessor( GetMockedReceiverConnection(), "entityPath", new ServiceBusSessionProcessorOptions()); Assert.That(async() => await processor.StartProcessingAsync(), Throws.InstanceOf <InvalidOperationException>()); }
public void MustSetErrorHandler() { var processor = new ServiceBusSessionProcessor( GetMockedConnection(), "entityPath", new ServiceBusSessionProcessorOptions()); processor.ProcessMessageAsync += eventArgs => Task.CompletedTask; Assert.That(async() => await processor.StartProcessingAsync(), Throws.InstanceOf <InvalidOperationException>()); }
public void CannotAddNullHandler() { var processor = new ServiceBusSessionProcessor( GetMockedReceiverConnection(), "entityPath", new ServiceBusSessionProcessorOptions()); Assert.That(() => processor.ProcessMessageAsync += null, Throws.InstanceOf <ArgumentNullException>()); Assert.That(() => processor.ProcessErrorAsync += null, Throws.InstanceOf <ArgumentNullException>()); Assert.That(() => processor.SessionInitializingAsync += null, Throws.InstanceOf <ArgumentNullException>()); Assert.That(() => processor.SessionClosingAsync += null, Throws.InstanceOf <ArgumentNullException>()); }
public void CannotAddTwoHandlersToTheSameEvent() { var processor = new ServiceBusSessionProcessor( GetMockedReceiverConnection(), "entityPath", new ServiceBusSessionProcessorOptions()); processor.ProcessMessageAsync += eventArgs => Task.CompletedTask; processor.ProcessErrorAsync += eventArgs => Task.CompletedTask; processor.SessionInitializingAsync += eventArgs => Task.CompletedTask; processor.SessionClosingAsync += eventArgs => Task.CompletedTask; Assert.That(() => processor.ProcessMessageAsync += eventArgs => Task.CompletedTask, Throws.InstanceOf <NotSupportedException>()); Assert.That(() => processor.ProcessErrorAsync += eventArgs => Task.CompletedTask, Throws.InstanceOf <NotSupportedException>()); Assert.That(() => processor.SessionInitializingAsync += eventArgs => Task.CompletedTask, Throws.InstanceOf <NotSupportedException>()); Assert.That(() => processor.SessionClosingAsync += eventArgs => Task.CompletedTask, Throws.InstanceOf <NotSupportedException>()); }
public static async Task RunSessionProcessor(string connectionString, string queueName, TimeSpan timeSpan) { // since ServiceBusClient implements IAsyncDisposable we create it with "await using" await using var client = new ServiceBusClient(connectionString); // get the options to use for configuring the processor var options = new ServiceBusSessionProcessorOptions { // By default after the message handler returns, the processor will complete the message // If I want more fine-grained control over settlement, I can set this to false. AutoCompleteMessages = false, // I can also allow for processing multiple sessions MaxConcurrentSessions = 5, // By default, there will be a single concurrent call per session. I can // increase that here to enable parallel processing within each session. MaxConcurrentCallsPerSession = 2 }; // create a processor that we can use to process the messages ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; await processor.StartProcessingAsync(); // since the message handler will run in a background thread, in order to prevent // this sample from terminating immediately DateTime endProcessing = DateTime.Now.Add(timeSpan); while (DateTime.Now < endProcessing) { await Task.Delay(100); } // stop processing once the task completion source was completed. await processor.StopProcessingAsync(); }
public void ConcurrencyUpdateManager_Sessions_UpdatesProcessorConcurrency() { var concurrencyOptions = new OptionsWrapper <ConcurrencyOptions>(new ConcurrencyOptions { DynamicConcurrencyEnabled = true }); var concurrencyManager = new ConcurrencyManager(concurrencyOptions, _loggerFactory, _mockConcurrencyThrottleManager.Object); _mockConcurrencyThrottleManager.Setup(p => p.GetStatus()).Returns(new ConcurrencyThrottleAggregateStatus { State = ThrottleState.Disabled }); ServiceBusSessionProcessor sessionProcessor = _client.CreateSessionProcessor(_entityPath, new ServiceBusSessionProcessorOptions { MaxConcurrentSessions = 1, MaxConcurrentCallsPerSession = 1 }); Lazy <SessionMessageProcessor> sessionMessageProcessor = new Lazy <SessionMessageProcessor>(() => new SessionMessageProcessor(sessionProcessor)); ILogger logger = _loggerFactory.CreateLogger("test"); ServiceBusListener.ConcurrencyUpdateManager concurrencyUpdateManager = new ServiceBusListener.ConcurrencyUpdateManager(concurrencyManager, null, sessionMessageProcessor, true, _functionId, logger); // when no messages are being processed, concurrency is not adjusted Assert.AreEqual(1, sessionProcessor.MaxConcurrentSessions); Assert.AreEqual(1, sessionProcessor.MaxConcurrentCallsPerSession); SetFunctionCurrentConcurrency(concurrencyManager, _functionId, 10); concurrencyUpdateManager.UpdateConcurrency(); Assert.AreEqual(1, sessionProcessor.MaxConcurrentSessions); Assert.AreEqual(1, sessionProcessor.MaxConcurrentCallsPerSession); // ensure processor concurrency is adjusted up concurrencyUpdateManager.MessageProcessed(); concurrencyUpdateManager.UpdateConcurrency(); Assert.AreEqual(10, sessionProcessor.MaxConcurrentSessions); Assert.AreEqual(1, sessionProcessor.MaxConcurrentCallsPerSession); // ensure processor concurrency is adjusted down SetFunctionCurrentConcurrency(concurrencyManager, _functionId, 5); concurrencyUpdateManager.MessageProcessed(); concurrencyUpdateManager.UpdateConcurrency(); Assert.AreEqual(5, sessionProcessor.MaxConcurrentSessions); Assert.AreEqual(1, sessionProcessor.MaxConcurrentCallsPerSession); }
public async Task PluginsSessionProcessor() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { #region Snippet:End2EndPluginSessionProcessor #if SNIPPET string connectionString = "<connection_string>"; string queueName = "<queue_name>"; // since ServiceBusClient implements IAsyncDisposable we create it with "await using" await using var client = new ServiceBusClient(connectionString); #else await using var client = CreateClient(); string queueName = scope.QueueName; #endif await using ServiceBusSender sender = client.CreatePluginSender(queueName, new List <Func <ServiceBusMessage, Task> >() { message => { message.Subject = "Updated subject"; message.SessionId = "sessionId"; #if SNIPPET Console.WriteLine("First send plugin executed!"); #endif return(Task.CompletedTask); }, message => { #if SNIPPET Console.WriteLine(message.Subject); // prints "Updated subject" Console.WriteLine(message.SessionId); // prints "sessionId" Console.WriteLine("Second send plugin executed!"); #else Assert.AreEqual("Updated subject", message.Subject); Assert.AreEqual("sessionId", message.SessionId); #endif return(Task.CompletedTask); }, }); await sender.SendMessageAsync(new ServiceBusMessage(Encoding.UTF8.GetBytes("First"))); #if !SNIPPET TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); #endif await using ServiceBusSessionProcessor processor = client.CreatePluginSessionProcessor(queueName, new List <Func <ServiceBusReceivedMessage, Task> >() { message => { var rawMessage = message.GetRawAmqpMessage(); rawMessage.Properties.Subject = "Received subject"; #if SNIPPET Console.WriteLine("First receive plugin executed!"); #endif return(Task.CompletedTask); }, message => { #if SNIPPET Console.WriteLine(message.Subject); // prints "Received subject" #else Assert.AreEqual("Received subject", message.Subject); #endif var rawMessage = message.GetRawAmqpMessage(); rawMessage.Properties.Subject = "Last subject"; #if SNIPPET Console.WriteLine("Second receive plugin executed!"); #endif return(Task.CompletedTask); }, }); processor.ProcessMessageAsync += args => { #if SNIPPET Console.WriteLine(args.Message.Subject); #else Assert.AreEqual("Last subject", args.Message.Subject); tcs.TrySetResult(true); #endif return(Task.CompletedTask); }; processor.ProcessErrorAsync += args => { #if SNIPPET Console.WriteLine(args.Exception); #endif return(Task.CompletedTask); }; await processor.StartProcessingAsync(); #if SNIPPET Console.ReadKey(); #else await tcs.Task; #endif #endregion }; }
public async Task ProcessSessionMessages() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { #region Snippet:ServiceBusProcessSessionMessages #if SNIPPET string connectionString = "<connection_string>"; string queueName = "<queue_name>"; // since ServiceBusClient implements IAsyncDisposable we create it with "await using" await using var client = new ServiceBusClient(connectionString); #else string connectionString = TestEnvironment.ServiceBusConnectionString; string queueName = scope.QueueName; await using var client = CreateClient(); #endif // create the sender ServiceBusSender sender = client.CreateSender(queueName); // create a message batch that we can send ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync(); messageBatch.TryAddMessage( new ServiceBusMessage("First") { SessionId = "Session1" }); messageBatch.TryAddMessage( new ServiceBusMessage("Second") { SessionId = "Session2" }); // send the message batch await sender.SendMessagesAsync(messageBatch); #region Snippet:ServiceBusConfigureSessionProcessor // create the options to use for configuring the processor var options = new ServiceBusSessionProcessorOptions { // By default after the message handler returns, the processor will complete the message // If I want more fine-grained control over settlement, I can set this to false. AutoCompleteMessages = false, // I can also allow for processing multiple sessions MaxConcurrentSessions = 5, // By default or when AutoCompleteMessages is set to true, the processor will complete the message after executing the message handler // Set AutoCompleteMessages to false to [settle messages](https://docs.microsoft.com/en-us/azure/service-bus-messaging/message-transfers-locks-settlement#peeklock) on your own. // In both cases, if the message handler throws an exception without settling the message, the processor will abandon the message. MaxConcurrentCallsPerSession = 2, // Processing can be optionally limited to a subset of session Ids. SessionIds = { "my-session", "your-session" }, }; // create a session processor that we can use to process the messages await using ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); // configure the message and error handler to use processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; async Task MessageHandler(ProcessSessionMessageEventArgs args) { var body = args.Message.Body.ToString(); // we can evaluate application logic and use that to determine how to settle the message. await args.CompleteMessageAsync(args.Message); // we can also set arbitrary session state using this receiver // the state is specific to the session, and not any particular message await args.SetSessionStateAsync(new BinaryData("some state")); } Task ErrorHandler(ProcessErrorEventArgs args) { // the error source tells me at what point in the processing an error occurred Console.WriteLine(args.ErrorSource); // the fully qualified namespace is available Console.WriteLine(args.FullyQualifiedNamespace); // as well as the entity path Console.WriteLine(args.EntityPath); Console.WriteLine(args.Exception.ToString()); return(Task.CompletedTask); } // start processing await processor.StartProcessingAsync(); // since the processing happens in the background, we add a Conole.ReadKey to allow the processing to continue until a key is pressed. Console.ReadKey(); #endregion #endregion } }
public SessionMessageProcessor(ServiceBusSessionProcessor processor) { Processor = processor ?? throw new ArgumentNullException(nameof(processor)); }
public async Task SessionProcessorActivities() { ClientDiagnosticListener.ProducedLink[] messageActivities = null; int messageProcessedCt = 0; bool callbackExecuted = false; _listener = new ClientDiagnosticListener( EntityScopeFactory.DiagnosticNamespace, scopeStartCallback: scope => { if (scope.Name == DiagnosticProperty.ProcessSessionMessageActivityName) { Assert.IsNotNull(messageActivities); Assert.AreEqual( messageActivities[messageProcessedCt], scope.Links.Single()); callbackExecuted = true; } }); await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { var client = new ServiceBusClient(TestEnvironment.ServiceBusConnectionString); ServiceBusSender sender = client.CreateSender(scope.QueueName); var messageCt = 2; var msgs = ServiceBusTestUtilities.GetMessages(messageCt, "sessionId"); await sender.SendMessagesAsync(msgs); Activity[] sendActivities = AssertSendActivities(false, sender, msgs); messageActivities = sendActivities.Select(a => new ClientDiagnosticListener.ProducedLink(a.ParentId, a.TraceStateString)).ToArray(); ServiceBusSessionProcessor processor = client.CreateSessionProcessor(scope.QueueName, new ServiceBusSessionProcessorOptions { AutoCompleteMessages = false, SessionIdleTimeout = TimeSpan.FromSeconds(10), MaxConcurrentSessions = 1 }); TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(); processor.ProcessMessageAsync += args => { if (++messageProcessedCt == messageCt) { tcs.SetResult(true); } return(Task.CompletedTask); }; processor.ProcessErrorAsync += ServiceBusTestUtilities.ExceptionHandler; await processor.StartProcessingAsync(); await tcs.Task; await processor.StopProcessingAsync(); for (int i = 0; i < messageCt; i++) { _listener.AssertAndRemoveScope(DiagnosticProperty.ReceiveActivityName); var processScope = _listener.AssertAndRemoveScope(DiagnosticProperty.ProcessSessionMessageActivityName); AssertCommonTags(processScope.Activity, processor.EntityPath, processor.FullyQualifiedNamespace); } Assert.IsTrue(callbackExecuted); } }
public async Task ProcessEventConsumesAllMessages(int numThreads, bool autoComplete) { await using (var scope = await ServiceBusScope.CreateWithQueue( enablePartitioning: false, enableSession: true)) { await using var client = GetClient(); ServiceBusSender sender = client.GetSender(scope.QueueName); // send 1 message for each thread and use a different session for each message ConcurrentDictionary <string, bool> sessions = new ConcurrentDictionary <string, bool>(); for (int i = 0; i < numThreads; i++) { var sessionId = Guid.NewGuid().ToString(); await sender.SendAsync(GetMessage(sessionId)); sessions.TryAdd(sessionId, true); } int messageCt = 0; TaskCompletionSource <bool> taskCompletionSource = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); var options = new ServiceBusProcessorOptions { MaxConcurrentCalls = numThreads, AutoComplete = autoComplete }; ServiceBusSessionProcessor processor = GetNoRetryClient().GetSessionProcessor(scope.QueueName, options); processor.ProcessMessageAsync += ProcessMessage; processor.ProcessErrorAsync += ExceptionHandler; await processor.StartProcessingAsync(); async Task ProcessMessage(ProcessSessionMessageEventArgs args) { try { var message = args.Message; if (!autoComplete) { await args.CompleteAsync(message); } sessions.TryRemove(message.SessionId, out bool _); Assert.AreEqual(message.SessionId, args.SessionId); Assert.IsNotNull(args.SessionLockedUntil); } finally { var ct = Interlocked.Increment(ref messageCt); if (ct == numThreads) { taskCompletionSource.SetResult(true); } } } await taskCompletionSource.Task; await processor.StopProcessingAsync(); Assert.AreEqual(numThreads, messageCt); // we should have received messages from each of the sessions Assert.AreEqual(0, sessions.Count); // try receiving to verify empty // since all the messages are gone and we are using sessions, we won't actually // be able to open the Receive link // only do this assertion when we complete the message ourselves, // otherwise the message completion may have been cancelled if it didn't finish // before calling StopProcessingAsync. if (!autoComplete) { Assert.That(async() => await GetNoRetryClient().GetSessionReceiverAsync(scope.QueueName), Throws.Exception); } } }
public async Task ProcessSessionMessages() { await using (var scope = await ServiceBusScope.CreateWithQueue(enablePartitioning: false, enableSession: true)) { string connectionString = TestEnvironment.ServiceBusConnectionString; string queueName = scope.QueueName; await using var client = GetClient(); #region Snippet:ServiceBusProcessSessionMessages //@@ string connectionString = "<connection_string>"; //@@ string queueName = "<queue_name>"; // since ServiceBusClient implements IAsyncDisposable we create it with "await using" //@@ await using var client = new ServiceBusClient(connectionString); // create the sender ServiceBusSender sender = client.CreateSender(queueName); // create a message batch that we can send ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync(); messageBatch.TryAddMessage( new ServiceBusMessage(Encoding.UTF8.GetBytes("First")) { SessionId = "Session1" }); messageBatch.TryAddMessage( new ServiceBusMessage(Encoding.UTF8.GetBytes("Second")) { SessionId = "Session2" }); // send the message batch await sender.SendMessagesAsync(messageBatch); // get the options to use for configuring the processor var options = new ServiceBusSessionProcessorOptions { // By default after the message handler returns, the processor will complete the message // If I want more fine-grained control over settlement, I can set this to false. AutoCompleteMessages = false, // I can also allow for processing multiple sessions MaxConcurrentSessions = 5, // By default, there will be a single concurrent call per session. I can // increase that here to enable parallel processing within each session. MaxConcurrentCallsPerSession = 2 }; // create a session processor that we can use to process the messages ServiceBusSessionProcessor processor = client.CreateSessionProcessor(queueName, options); // since the message handler will run in a background thread, in order to prevent // this sample from terminating immediately, we can use a task completion source that // we complete from within the message handler. TaskCompletionSource <bool> tcs = new TaskCompletionSource <bool>(TaskCreationOptions.RunContinuationsAsynchronously); int processedMessageCount = 0; processor.ProcessMessageAsync += MessageHandler; processor.ProcessErrorAsync += ErrorHandler; async Task MessageHandler(ProcessSessionMessageEventArgs args) { var body = args.Message.Body.ToString(); // we can evaluate application logic and use that to determine how to settle the message. await args.CompleteMessageAsync(args.Message); // we can also set arbitrary session state using this receiver // the state is specific to the session, and not any particular message await args.SetSessionStateAsync(new BinaryData("some state")); // Once we've received the last message, complete the // task completion source. if (Interlocked.Increment(ref processedMessageCount) == 2) { tcs.SetResult(true); } } Task ErrorHandler(ProcessErrorEventArgs args) { // the error source tells me at what point in the processing an error occurred Console.WriteLine(args.ErrorSource); // the fully qualified namespace is available Console.WriteLine(args.FullyQualifiedNamespace); // as well as the entity path Console.WriteLine(args.EntityPath); Console.WriteLine(args.Exception.ToString()); return(Task.CompletedTask); } await processor.StartProcessingAsync(); // await our task completion source task so that the message handler will be invoked at least once. await tcs.Task; // stop processing once the task completion source was completed. await processor.StopProcessingAsync(); #endregion } }
/// <summary> /// Initializes a new instance of <see cref="SessionMessageProcessor"/>. /// </summary> /// <param name="processor">The <see cref="ServiceBusSessionProcessor"/> to use for processing messages from Service Bus.</param> protected internal SessionMessageProcessor(ServiceBusSessionProcessor processor) { Processor = processor ?? throw new ArgumentNullException(nameof(processor)); }