Azure WebJobs SDK 3.0 vs. IHostedService

I’m moving some services to Azure, and looking at our options there. WebJobs 3.0 SDK lets us run continuous jobs (with some jankiness), but the resulting host is really weird looking. We wind up having:

var endpointInstance = await Endpoint.Start(endpointConfiguration).ConfigureAwait(false);

while (!cancellationToken.IsCancellationRequested)
{
    await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
}

await endpointInstance.Stop().ConfigureAwait(false);

In our host. It’s really odd.

Compare that with IHostedService, and we get explicit start/stop methods. Anyone tried to host IHostedService inside of the WebJobs host?

The pattern that you’ve used is aligned with WebJobs SDK 2.0.
With the SDK 3.0 it’s different. The documentation is not up yet, but you can have a look at the PR to see how to achieve the hosting with the SDK 3.0.

Ah so you’re not using the “Continuous” web job, but instead just taking the whole job host over?

Not sure what do you mean by that.
A WebJobs is a single job for a host. You don’t “take over” as your job is the only thing that will run. You can also host Functions in any given WebJob, but that’s not what you wanted. BTW, it was the same thing with the SDK 2.0 IIRC.

Jimmy the approach I pasted in the IServiceCollection discussion that uses hosted services would also work with WebJobs. With the sample PR above I went the more native approach to avoid ordering problems. When you look at azure-webjobs-sdk/JobHostService.cs at master · Azure/azure-webjobs-sdk · GitHub you can see that the web jobs SDK also leverages IHostedService to start the IJobHost. If you start NServiceBus within a hosted service yourself depending on what you want to achieve you have to make sure that it starts before the job host.

Continuous web jobs were a named trigger, decorated with “NoAutomaticTrigger” then you had to call that job explicitly:

https://docs.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to#manual-triggers

You can mix triggered jobs and the continuous one in the same exe.

With the “NoAutomaticTrigger”, it’s still a named trigger that shows up in the console and Kudu explicitly, for example.

Here’s an example:

var host = builder.Build();
using (host)
{
    await host.StartAsync();
    var jobHost = host.Services.GetService<IJobHost>();
    await jobHost.CallAsync(nameof(Host.RunAsync));
}

Then the job itself:

public class Host
{
    private readonly ILogger<Host> _logger;
    private readonly IConfiguration _configuration;
    private readonly ILoggerFactory _loggerFactory;

    public Host(ILogger<Host> logger, IConfiguration configuration, ILoggerFactory loggerFactory)
    {
        _logger = logger;
        _configuration = configuration;
        _loggerFactory = loggerFactory;
    }

    [NoAutomaticTrigger]
    public async Task RunAsync(CancellationToken cancellationToken)
    {
        Console.Title = "NsbAzureHosting.WebJobReceiver";

        _logger.LogInformation("Running");

        var logFactory = LogManager.Use<MicrosoftLogFactory>();
        logFactory.UseMsFactory(_loggerFactory);

        var endpointConfiguration = new EndpointConfiguration("NsbAzureHosting.WebJobReceiver");
        endpointConfiguration.UseSerialization<NewtonsoftSerializer>();
        endpointConfiguration.EnableInstallers();
        endpointConfiguration.UsePersistence<LearningPersistence>();

        var transport = endpointConfiguration.UseTransport<AzureServiceBusTransport>();
        var connectionString = _configuration.GetConnectionString("AzureServiceBus");
        transport.ConnectionString(connectionString);

        var endpointInstance = await Endpoint.Start(endpointConfiguration).ConfigureAwait(false);

        while (!cancellationToken.IsCancellationRequested)
        {
            await Task.Delay(3000, cancellationToken).ConfigureAwait(false);
        }

        await endpointInstance.Stop().ConfigureAwait(false);
    }
}

It’s a bit weird but I’m mixing the “NoAutomaticTrigger” kind of job from the SDK.

I don’t like it btw.

FYI we merged the doco sample

Have a nice weekend Jimmy!

After downloading and playing around with the WebJobs SDK 3.0 sample found here: Self-Hosting in Azure WebJobs • NServiceBus Samples • Particular Docs

I’m struggling with how/where to wire dependency injection up for injection into handlers. I’ve tried in HostBuilderExtensions.ConfigureServices:
image

As well as in ContinousJob.StartAsync using both the injected IServiceCollection as well as trying to leverage endpointConfiguration.RegisterComponents();

When I try to use IServiceCollection from either class, I get the following exception:

2020-02-25 16:24:46.769 WARN  NServiceBus.Transport.AzureStorageQueues.ReceiveStrategy Azure Storage Queue transport failed pushing a message through pipeline. The message will be requeued
System.InvalidOperationException: Unable to resolve type: MyMessageHandler, service name:
 ---> System.InvalidOperationException: Unresolved dependency [Target Type: MyMessageHandler], [Parameter: doSomethingUseful(Receiver.IDoSomethingUseful)], [Requested dependency: ServiceType:Receiver.IDoSomethingUseful, ServiceName:]

When using endpointConfiguration.RegisterComponents(), the handler’s constructor that takes the dependency is completely skipped (but the handler still fires).

I’m still very new to .NET Core, and more specifically, the Generic Host. .NET Core has changed between .NET Core 2.x/3.x in combinations with the WebJobs SDK changes between 2.x/3.x as well.

Thanks!

hey @mgmccarthy,

If I understand correctly your case, the ASP.NET Core 3 Dependency Injection Integration should be what you are looking for.

In essence:

Cheers,
.m

1 Like

Hi Michael

I’ve PR some tweaks to the webjobs sample

That shows how to integrate NServiceBus.Extension.Hosting into web jobs

Hope that helps

If not let me know

Regards
Daniel

1 Like

@danielmarbach and @mauroservienti. The PR works. Thanks for help.

Question on the NoOpJobHost. Is that necessary to keep? To me, it looks like the only thing that indicates that the hosted service is a WebJob.

@mgmccarthy

If you don’t add it the web jobs runtime will simply log a warning but it will continue to work. I added it to not confuse people that are running the sample with the warning

1 Like