Any plans to create a web new template for the new Worker Service?

I’m interested in building a long running queue processor with Service Stack using the new Core 3.0 Worker Service template and would be nice to have a quick start template available.

My guess is this is similar to web new selfhost but I’m assuming there is something “more” provided using the Worker Service when it comes to system level hooks when running either as a Windows Service or Linux Daemon.

Which ASP.NET Core Worker Service template are you referring to?

It’s not ASP.NET. I guess it would be considered a “Console” app?

All ASP .NET Core Apps are Console Apps.

Just had a look at this Worker Service template and this is their background worker example:

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
        }
    }
}

It’s running a background task not a HTTP Server so I’m not sure what ServiceStack is supposed to be doing here? What use-case were you planning on using ServiceStack here for?

So I’m trying to build a queue processor, nothing more. I don’t want the added weight of any http stuff as it will simply sit there processing incoming messages from SQS. I’m attracted to this new Worker Service because it seems to have tighter OS level integration with regards to service startup and shutdown.

Because I like ServiceStack so much, I was hoping to use it for this. This Worker Service would handle SQS messages for typed dto’s and then possibly do stuff with AutoQuery/Ormlite to “process” the message.

I’ve added a worker-rabbitmq template based on the .NET Core 3.0 worker service project which runs an headless ServiceStack AppHost and a RabbitMQ Worker background Service which starts the MQ Server and dumps the MQ Stats every 10s until the request is cancelled where it will stop the MQ Server.

There’s an example of calling the Rabbit MQ Server in the MqTest project:

using (var mqClient = MqFactory.CreateMessageQueueClient())
{
    var replyToMq = mqClient.GetTempQueueName();

    mqClient.Publish(new Message<Hello>(new Hello { Name = "MQ Worker" })
    {
        ReplyTo = replyToMq,
    });

    var responseMsg = mqClient.Get<HelloResponse>(replyToMq);
    mqClient.Ack(responseMsg);
    Assert.That(responseMsg.GetBody().Result, Is.EqualTo("Hello, MQ Worker!"));
}

It should be easy to swap out RabbitMQ to use your preferred MQ Server.

You can create a project from this template using web new:

$ web new worker-rabbitmq MqTest

Thank you so much for doing this so quickly!

Ok, I created the sample project with web new worker-rabbitmq MqTest. I swapped out RabbitMQ for SQS with the following:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureServices((hostContext, services) =>
            {
                AppHost = new BasicAppHost(typeof(MyService).Assembly)
                {
                    ConfigureAppHost = host =>
                    {
                        //var mqServer = new RabbitMqServer(hostContext.Configuration.GetConnectionString("RabbitMq") ?? "localhost:5672");
                        //mqServer.RegisterHandler<Hello>(host.ExecuteMessage);
                        //host.Register<IMessageService>(mqServer);

                        var mqServer = new SqsMqServer(
                            "access_key",
                            "secret_key",
                            RegionEndpoint.USWest2)
                        {
                            DisableBuffering = true, // Trade-off latency vs efficiency
                        };
                        mqServer.RegisterHandler<Hello>(host.ExecuteMessage);
                        host.Register<IMessageService>(mqServer);
                    }
                }.Init();

                services.AddSingleton(AppHost.Resolve<IMessageService>());
                services.AddHostedService<MqWorker>();
            });

But when it picks up an already published message from the mq-Hello-inq queue, it immediately moves it to mq-Hello-dlq but this time there is no error attribute on the message, so I can’t figure out why it’s failing. I attempted to debug by placing multiple breakpoints within Visual Studio but none of them get triggered, so I’m not sure what’s going on. Do you have any idea where the problem may be?

Add a ResponseStatus property to HelloResponse to include error responses:

public class HelloResponse
{
    public string Result { get; set; }
    public ResponseStatus ResponseStatus { get; set; }
}

It seems that adding this made it work. It’s now properly going to the mq-HelloResponse-inq queue.

This is great! Thanks!

It shouldn’t have made it work, but it should now capture the Error of why it failed though.

Well, the interesting thing is, simply commenting out this line results in it going right back to the dlq. Uncommenting it, makes it go to the response inq.

Or, maybe not. Not sure what was going on, but it seems like now it does not go to the dlq. Have no idea. Just glad it’s working. Thanks.

I’m attempting to add OrmLite capability to this service. I used

mix postgres

It added a Configure.Db class. However, it doesn’t seem to be getting used by this worker service. I have used mix … in a regular web based app and the Configure.X seems to be auto-wired, but not in this case.

What am I missing to get Configure.Db wired up in this worker service?

The Worker Services doesn’t support Modular Startup or .NET Core’s Startup class for that matter, you’d have to configure it normally, i.e:

ConfigureAppHost = host => {
    //...
    host.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(
        hostContext.Configuration.GetConnectionString("DefaultConnection"),
        PostgreSqlDialect.Provider));
}

And add a reference to ServiceStack.OrmLite.PostgreSQL NuGet package if not already, although mix postgres should’ve added it.

That worked. However, I need to implement something similar to the RegisterTypedRequestFilter so that I can set the actual database connection per “environment” as it comes across as a property in the message.

Since I’m assuming the request handler is not an actual “request”, it seems this code never gets executed so I have no chance to set the connection string for that environment.

How can I do something similar to the below in the Worker Service?

RegisterTypedRequestFilter<IForEnvironment>((req, res, requestDto) =>
{
    if (requestDto.Environment == Guid.Empty)
        throw new ArgumentNullException("Environment");
    req.Items[Keywords.DbInfo] = new ConnectionInfo() { ConnectionString = GetEnvironmentConnString(dbFactory, requestDto.Environment) };
});

You can use the Global Message Fitlers.

Please be aware that there’s no HTTP Server in Worker Services so none of the HTTP-specific features/functionality is going to be available.

I ended up using the RegisterTypedMessageRequestFilter option. I guess it wasn’t hitting the other one since it wasn’t “message” based.

Works like a charm!