0

When I use the code below, the Web API does not start, I don't see the swagger page. Do you have an idea why ? Swagger starts normally when I comment the code in ConfigurationServices. I'd like have the normal behaviour (means see all endpoint from the controller) + subscribe a method to the queue.

public class Startup
{
    static QueueClient queueClient;

    public void ConfigureServices(IServiceCollection services)
    {
        try
        {
            queueClient = new QueueClient("BusConnectionString","QueueName");
            var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
            {
                MaxConcurrentCalls = 1,
                AutoComplete = false
            };
            queueClient.RegisterMessageHandler(ReceiveMessagesAsync, messageHandlerOptions);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            Console.ReadKey();
            queueClient.CloseAsync();
        }
    }

    static async Task ReceiveMessagesAsync(Message message, CancellationToken token)
    {
        Console.WriteLine($"Received message: {Encoding.UTF8.GetString(message.Body)}");

        await queueClient.CompleteAsync(message.SystemProperties.LockToken);
    }

    static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
    {
        Console.WriteLine(exceptionReceivedEventArgs.Exception);
        return Task.CompletedTask;
    }
}

Below the code updated I hit the DoWork method every 5 seconds but I don't receive any message in ReceiveMessagesAsync and no exception

Update 1 :

public class TimedHostedService : IHostedService, IDisposable
{
    static QueueClient queueClient;

    Timer timer;

    readonly IConfiguration configuration;

    public TimedHostedService(IConfiguration configuration)
    {
        this.configuration = configuration;
    }

    public Task StartAsync(CancellationToken stoppingToken)
    {
        timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    void DoWork(object state)
    {
        try
        {
            queueClient = new QueueClient("QueueConnectionString", "QueueName"]);
            var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
            {
                MaxConcurrentCalls = 1,
                AutoComplete = false
            };
            queueClient.RegisterMessageHandler(ReceiveMessagesAsync, messageHandlerOptions);
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            queueClient.CloseAsync();
        }

    }

    public Task StopAsync(CancellationToken stoppingToken)
    {

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        timer?.Dispose();
    }

    static async Task ReceiveMessagesAsync(Message message, CancellationToken token)
    {
        Console.WriteLine($"Received message: {Encoding.UTF8.GetString(message.Body)}");

        await queueClient.CompleteAsync(message.SystemProperties.LockToken);
    }

    static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
    {
        Console.WriteLine(exceptionReceivedEventArgs.Exception);
        return Task.CompletedTask;
    }

}
Chris Patterson
  • 28,659
  • 3
  • 47
  • 59
TheBoubou
  • 19,487
  • 54
  • 148
  • 236
  • Any exceptions? This would happen if the application crashed. Frankly there's no information here so one can only guess. Perhaps the URL is wrong. Perhaps the credentials are wrong. Or perhaps the application blocks. – Panagiotis Kanavos Aug 27 '21 at 07:51
  • This seems like code that should be in a hosted service, not in ConfigureServices. – juunas Aug 27 '21 at 07:51
  • Grrrr I removed the Console.ReadKey(); Swagger is started but when I create a message I don't receive anything in 'ReceiveMessagesAsync' – TheBoubou Aug 27 '21 at 07:56
  • Move the queue code to a background process, see https://learn.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice#implementing-ihostedservice-with-a-custom-hosted-service-class-deriving-from-the-backgroundservice-base-class – Peter Bons Aug 27 '21 at 08:02
  • @PeterBons The method 'ReceiveMessagesAsync' not the Azure configuration queue ? – TheBoubou Aug 27 '21 at 08:16
  • In the background process you need to create QueueClient and do the message handling. – Peter Bons Aug 27 '21 at 08:42
  • @PeterBons I updated the code, I hit the DoWork every x second but nothing coming from queue – TheBoubou Aug 27 '21 at 13:28

1 Answers1

1

You shouldn't do long running work in the startup phase. The initial code blocks executing by doing Console.ReadKey() in the startup phase.

Long running work should be done in the background.But not using a timer because now you spin up a new listener on every tick, and close it almost directly. Try this:

public class QueueProcessorService : IHostedService
{
    private QueueClient queueClient;
    
    public Task StartAsync(CancellationToken cancellationToken)
    {
        queueClient = new QueueClient("BusConnectionString", "QueueName");
        var messageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
        {
            MaxConcurrentCalls = 1,
            AutoComplete = false
        };
        queueClient.RegisterMessageHandler(ReceiveMessagesAsync, messageHandlerOptions);
        
        return Task.CompletedTask;
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        await queueClient.CloseAsync();
    }

    async Task ReceiveMessagesAsync(Message message, CancellationToken token)
    {
        Console.WriteLine($"Received message: {Encoding.UTF8.GetString(message.Body)}");

        await queueClient.CompleteAsync(message.SystemProperties.LockToken);
    }

    Task ExceptionReceivedHandler(ExceptionReceivedEventArgs exceptionReceivedEventArgs)
    {
        Console.WriteLine(exceptionReceivedEventArgs.Exception);
        return Task.CompletedTask;
    }
}

By the way, it seems like you are using a rather outdated package Microsoft.Azure.ServiceBus instead of the recommended one (Azure.Messaging.ServiceBus).

Peter Bons
  • 26,826
  • 4
  • 50
  • 74
  • When I pass as the Context (configurated in startup.cs of course) in the constructor, I get this exception "ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType" I think I tried all solution found on the web :( – TheBoubou Aug 30 '21 at 11:44
  • Can you post the configuration of `Context`? – Peter Bons Aug 30 '21 at 11:45
  • Here you are : services.AddDbContext( options => options.UseSqlServer( Configuration.GetConnectionString("MyConnectionName"))); – TheBoubou Aug 30 '21 at 11:50
  • Thing is, it is registered as a scoped service. I'd suggest taking a like at [this answer](https://stackoverflow.com/questions/51572637/access-dbcontext-service-from-background-task/) – Peter Bons Aug 30 '21 at 11:53