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
- Getting Started
- Data Access EF Core
- Azure WebJob (Current)
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
- Record a download all books request.
- Trigger a background job to handle the request: Read all books and create a zip file.
- 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:
- Can consume those settings. So I can manage application settings at one place, and change at wish without redeployment.
- 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
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>(); } }
- First, create a ServiceCollection and configure it with all dependencies. Pay attention to the use of AddEnvironmentVariables()
- Second, create a custom IJobActivator: ServiceCollectionJobActivator
- 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>.
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
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.