Azure-webjobs-sdk: Continuous Web Jobs in 3.0 - how to?

Created on 2 Oct 2018  路  9Comments  路  Source: Azure/azure-webjobs-sdk

In 2.0, you could have timer and queue triggered jobs, along with a continuous job;

            var host = new JobHost(config);
            host.CallAsync(typeof(WebJobsMethods).GetMethod("ProcessMethod"));
            host.RunAndBlock();

This doesn't seem to work in 3.0
How do you run continuous webjobs (a loop) in 3.0?

Most helpful comment

A colleague and I figured it out. You will need to do the HostBuilder approach above, but replace the using block with this:

C# using (host) { await host.StartAsync(); var jobHost = host.Services.GetService<IJobHost>(); await jobHost.CallAsync("YourFunctionNameHere"); }

In addition, you may need to add a [FunctionName("YourFunctionNameHere")] attribute to your [NoAutomaticTrigger] function.

All 9 comments

I really need to know how to achieve the same in 3.0. I just wonder why isn't this issue not yet addressed? This should have more upvotes.!!

I'm using the following code and it seems to be working locally. Still having some issues when deploying to Azure, my timer triggers don't seem to hit, but in regards to your question on how to run continuously, this should help.

```c#
public static async Task Main(string[] args)
{
IConfiguration configuration = null;

        var hostBuilder = new HostBuilder()

// configure host
;

        var host = hostBuilder.Build();

        using (host)
        {
            await host.RunAsync();
        }
    }

```

@Veflow - Thanks for the suggestion, and that definitely allows the _host_ to run continuously, but it doesn't alone solve the problem. In your example, any continuous/manual functions with [NoAutomaticTrigger] are not called. The prior SDK had a CallAsync method as shown in the top example that allowed you to explicitly make the host call the function.

A colleague and I figured it out. You will need to do the HostBuilder approach above, but replace the using block with this:

C# using (host) { await host.StartAsync(); var jobHost = host.Services.GetService<IJobHost>(); await jobHost.CallAsync("YourFunctionNameHere"); }

In addition, you may need to add a [FunctionName("YourFunctionNameHere")] attribute to your [NoAutomaticTrigger] function.

Nice one :)

I can confirm I needed to add a FunctionName attribute, and to keep the code as strongly-typed as possible I used nameof() rather than a string, i.e.

var host = builder.Build();
using (host)
{
    //await host.RunAsync();
    await host.StartAsync();
    var jobHost = host.Services.GetService<IJobHost>();
    await jobHost.CallAsync(nameof(MyFunctionsClass.MyContinuousMethod));
}
[FunctionName(nameof(MyFunctionsClass.MyContinuousMethod))]
[NoAutomaticTrigger]
public async Task MyContinuousMethod()
{
    await Task.Delay(0);
    //do stuff here...
}

I'm failing to use the solution @paulirwin (I'm in a dotnet console app and done this in the "old way numerous time").

var jobHost = host.Services.GetService<IJobHost>();

return null, I tried with a host.Start() before rather await host.StartAsync() and same null result ...

any advice ?

I end up addressing it with following code, fairlysure it's not clean ... open to suggestion (not really comfortable with async in dotnet).

class Program
    {
        static void Main(string[] args)
        {
            var host = new HostBuilder().ConfigureServices(services => services.AddSingleton<IJobHost>(new Continuous())).Build();

            var cancellationToken = new WebJobsShutdownWatcher().Token;
            cancellationToken.Register(() =>
            {
                host.Services.GetService<IJobHost>().StopAsync();
                // bye bye
            });

            using (host)
            {
                var result = host.Services.GetService<IJobHost>().StartAsync(cancellationToken);
            }
        }
    }

    class Continuous : IJobHost
    {
        Task StartAsync(CancellationToken cancellationToken)
        {
            do
            {
                // whatever
            } while (!cancellationToken.IsCancellationRequested);
            return Task.CompletedTask;
        }

        Task StopAsync()
        {
            // gracefull shutdown
            return Task.CompletedTask;
        }
    }

@camous, I think IJobHost is not resolving because your example is missing a host.ConfigureWebJobs() call.

Sorry, I missed the previous email. @smichtch is correct, I am calling ConfigureWebJobs. Here's my full code before the code snippet I posted earlier, with some IP redactions of course.

```C#
string environment = "Production";

        if (args != null && args.Length > 0 && args[0] == "local")
        {
            environment = "Development";
        }

        var builder = new HostBuilder()
            .UseEnvironment(environment)
            .ConfigureWebJobs(b => { })
            .ConfigureAppConfiguration(b =>
            {
                b.AddCommandLine(args);
                b.AddEnvironmentVariables();
                b.AddJsonFile("appsettings.json", optional: true);
                b.AddJsonFile($"appsettings.{environment.ToLower()}.json", optional: true);
            })
            .ConfigureLogging((context, b) =>
            {
                b.SetMinimumLevel(context.HostingEnvironment.IsProduction() ? LogLevel.Information : LogLevel.Debug);
                b.AddConsole();

                string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
                if (!string.IsNullOrEmpty(appInsightsKey))
                {
                    b.AddApplicationInsights(o => o.InstrumentationKey = appInsightsKey);
                }
            })
            .ConfigureServices((context, services) =>
            {
                // ... DI registration of services here
            })
            .UseConsoleLifetime();

        var host = builder.Build();

```

Was this page helpful?
0 / 5 - 0 ratings