Azure WebJob Getting Started

After having ASP.NET Core website setup, having basic EF Core in place, let’s have some fun with Azure WebJob.

Journey to Azure

At the simplest form, WebJob is a background service (think of Windows Service) running alongside with the WebSite (or Web Application). The abstraction usage is that it will handle the long running jobs for web application, therefore, free the web application to serve as many requests as possible.

From the book or abstraction level thinking, think of a scenario where there is a book management website. A user has many books. One day, he wants to download all his books in a zip file. Assuming that the action will take time, in a matter of minutes, therefore, you do not want your users to wait. At the high design level, there are steps

  1. Record a download all books request.
  2. Trigger a background job to handle the request: Read all books and create a zip file.
  3. Email the user with a link to download the zip file.

#1 and #3 are handled via web application. #2 is a good candidate for a WebJob. There are other ways to implement #2. But, in the context of this post, it is a WebJob.

That is the overview, the abstract level. Looks simple and easy to understand. But, hmm everything has a but, devil is at the detail. Let’s get our hands dirty in the code.

Context

Everything has its own context. Here they are

Given that there is an ASP.NET Core 2.0 website running in Azure App Service. Configuration settings, connection strings are configured using portal dashboard

I want to be able to build a WebJob that:

  1. Can consume those settings. So I can manage application settings at one place, and change at wish without redeployment.
  2. Take advantages of Dependency Inject from Microsoft.Extensions (the same as ASP.NET Core application)

Simple like that!

Environment and Code

Visual Studio Enterprise 2017

Version 15.6.4

.NET Framework 4.7.02556

If you are using a different environment, some default settings might be different.

Add a WebJob project from VS installed templates

Visual Studio Azure WebJob
Visual Studio Azure WebJob

All the detail is here at MS docs.

I found a perfect post that has what I need. To implement the Dependency Inject, I need to tell JobHostConfiguration to use my custom JobActivator.

NuGet packages

Microsoft.Extensions.Configuration

Microsoft.Extensions.DependencyInjection

Microsoft.Extensions.Configuration.Json

Microsoft.Extensions.Options.ConfigurationExtensions

Microsoft.Extensions.Configuration.EnvironmentVariables

Before showing code, you must install those packages using NuGet, I prefer using Package Manager Console. Tips: Type “Tab” to use auto complete.

    class Program
    {
        // Please set the following connection strings in app.config for this WebJob to run:
        // AzureWebJobsDashboard and AzureWebJobsStorage
        static void Main()
        {
            IServiceCollection serviceCollection = new ServiceCollection();
            ConfigureServices(serviceCollection);
            var config = new JobHostConfiguration
            {
                JobActivator = new ServiceCollectionJobActivator(serviceCollection.BuildServiceProvider())
            };
            if (config.IsDevelopment)
            {
                config.UseDevelopmentSettings();
            }
            
            // See full trigger extensions https://github.com/Azure/azure-webjobs-sdk-extensions/blob/master/README.md 
            config.UseTimers();
            var host = new JobHost(config);
            
            // The following code ensures that the WebJob will be running continuously
            host.RunAndBlock();
        }
        /// <summary>
        /// https://matt-roberts.me/azure-webjobs-in-net-core-2-with-di-and-configuration/
        /// </summary>
        /// <param name="serviceCollection"></param>
        private static void ConfigureServices(IServiceCollection serviceCollection)
        {
            var config = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", optional:true, reloadOnChange:true)
                .AddEnvironmentVariables()
                .Build();
            serviceCollection.AddOptions();
            serviceCollection.Configure<AppSetting>(config);
            // Configure custom services
            serviceCollection.AddScoped<Functions>();
        }
    }
  1. First, create a ServiceCollection and configure it with all dependencies. Pay attention to the use of AddEnvironmentVariables()
  2. Second, create a custom IJobActivator: ServiceCollectionJobActivator
  3. Wire them up
    public class ServiceCollectionJobActivator : IJobActivator
    {
        private readonly IServiceProvider _serviceProvider;

        public ServiceCollectionJobActivator(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        public T CreateInstance<T>()
        {
            return _serviceProvider.GetService<T>();
        }
    }

A very simple implementation. What it does is telling the JobHostConfiguration to use IServiceProvider (supply from ServiceCollection) to create instances.

And I have DI at will

    public class Functions
    {
        private readonly AppSetting _settings;

        public Functions(IOptions<AppSetting> settingAccessor)
        {
            _settings = settingAccessor.Value;
        }
        public void FetchTogglTimeEntry([TimerTrigger("00:02:00")] TimerInfo timer, TextWriter log)
        {
            log.WriteLine("Toggl job settings: {0}", _settings.TogglJobSettings.Url);
        }
    }

    public class AppSetting
    {
        public TogglJobSettings TogglJobSettings { get; set; }
    }
    public class TogglJobSettings
    {
        public string Url { get; set; }
        public string SecretKey { get; set; }
    }

The Functions class accept IOptions<AppSetting> injected into its constructor, just like an MVC controller.

I just want to have only TogglJobSettings. However, it does not work if injected IOptions<TogglJobSettings>.

Application Settings
Application Settings

Looking at the syntax (having __ in the keys), they should have been correct with the binding. However, whatever in the Application settings, there will be APPSETTING_ prefix, by looking at the environment variables.

Go to Azure Website, access Console, type env to see all environment variables

Azure Console Evn
Azure Console View Environment Variables

Pretty cool tool 🙂

By creating a top level class AppSetting (think of appSettings in web.config), things just work out of the box.

Wrap Up

Once having them setup, I can start writing business code. The WebJobs SDK and its extensions supply many ways of triggering a job. The DI infrastructure setup might happen once, and reuse (copy and paste) many times in other WebJobs; however, I gain so much confident and knowledge when getting my hands on code. Well, actually, it is always a good, right way of learning anything.

If you are learning Azure, I suggest you open Visual Studio and start typing.

Write a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.