Upgrading to WebJobs SDK 3.0
With the recent release of Azure Functions Runtime 2.0, WebJobs SDK 3.0 got released alongside. WebJobs SDK is the backbone of Azure Functions, however it can be also used standalone to power Azure WebJobs which you can host alongside your App Service. SDK 3.0 brings the configuration much closer to ASP.NET Core, runs under both .NET Framework and .NET Core and for example supports Dependency Injection by default.
SDK 3.0 was released a while ago, however there is no changelog available (yet) neither the documentation or the samples were upgraded yet. I decided to upgrade anyways, since the SDK is opensource so if I hit some issue, I can troubleshoot it myself. Luckily, this wasn't much of a case.
First off, you want to start with upgrading Nuget packages to the latest versions. Then, you will need to modify the Program.cs setup. Like mentioned above, SDK 3.0 brings WebJobs much closer to ASP.NET Core configuration, which is pretty nice. You might want to take a look at the only sample at the time of writing which is included with the SDK's source.
However, you can notice few things, which I would say should not made it into the sample:
First off, the Envionment is configured statically by UseEnvironment - which I really don't like. In ASP.NET Core, you can configure Environment by ASPNETCORE_ENVIRONMENT env variable, here this one won't work. Since ASP.NET Core uses WebHostBuilder and WebJobs use the HostBuilder, there are few differences: In order to specify environment, you need to use environment variable name, just like that.
Next, I really like the concept of User Secrets in ASP.NET Core for development, so why not use those here too? In order to do that, you will need to make two modifications. First, setup the UserSecretsId assembly attribute on the Program class:
[assembly: UserSecretsId("project-name")]
Thanks to this, the assembly will now contain the information about User Secrets. In ASP.NET Core, this is defined in .csproj - so far, I haven't found a way to do this with a WebJob - the build seems to ignore it. I will probably dedicate it a separate article. And then, you need to setup ConfigureHostConfiguration on the HostBuilder like so:
This is going to tell it to use Command Line arguments, Environment Variables and Secrets. Next up is the configuration of all the required triggers which you might be using in ConfigureWebJobs section:
.ConfigureHostConfiguration(config =>
{
config.AddCommandLine(args);
config.AddEnvironmentVariables();
config.AddUserSecrets<Program>();
})
// ...
.ConfigureWebJobs(config =>
{
config.AddAzureStorageCoreServices();
config.AddTimers();
})
Basically, AddAzureStorageCoreServices makes sure that your WebJob is hooked to a storage account for persisting data, creating logs etc. AddTimers is from WebJobs.Extensions package and allows you to periodically trigger some tasks. You can also use other extensions to connect to Event Grid, Service Bus etc.
Then you should configure logging by ConfigureLogging. In the sample, they don't check the environment and simple set the debug level to Debug, however since we set the environment previously, we can set it based on the environment:
.ConfigureLogging((context, config) =>
{
if (context.HostingEnvironment.IsDevelopment())
{
config.SetMinimumLevel(LogLevel.Debug);
config.AddConsole();
}
})
Then we need to configure the Dependency Injection if needed by ConfigureServices.
.ConfigureServices((context, services) =>
{
services.AddMemoryCache();
services.AddSingleton(context.Configuration);
services.AddSingleton();
services.AddScoped<Functions, Functions>();
})
Remember to always register your Functions class into services, if you don't do that, the depedendency injection will not work properly.
Here I have hit a thing which I need to investigate a bit further - the SDK 3.0 seems to support DI by default however, it doesn't seem to work:
Event tho the runtime registers DefaultJobActivator, it doesn't seem to resolve the services from the container and you end up with: System.MissingMethodException: No parameterless constructor defined for this object. error, so instead I decided to use my own IJobActivator implementation from SDK 2.0:
class JobActivator : IJobActivator
{
private readonly IServiceProvider _service;
public JobActivator(IServiceProvider service)
{
_service = service;
}
public T CreateInstance<T>()
{
return (T)_service.GetService(typeof(T));
}
}
You simply register it into the DI and it is going to work fine. I am not quite sure why the default activator doesn't work for me yet - I will investigate it and update the article if I end up with some results.
Last thing to do is to start using .NET Core logging which basically means, that you replace TextWriter with ILogger and use it just like in an ASP.NET Core app. You can leave TextWriter as is since it will work, however I suggest you switch to ILogger so that you have unified logging across the app. You can optionally add Application Insights if needed - those are part of the sample.
Comments
Pete Lavallee
I also haven’t been able to get the built in DI to work either.
Utkarsh Patel
How to register JobActivator using DI ? Could you please give sample code.
Jan Hajek
It’s actually super simple, just add: services..AddSingleton<IJobActivator, JobActivator>(); to ConfigureServices and you should be good to go.
Ed Baker
.NET Core WebJob SDK 3.x this is what my program looks like:
Pooja Dhingra
How we can run the web job when a new message encounters in a Queue?
Jan Hajek
Do you mean Service Bus Queue or Storage Queue?
SeboStone
I did the upgrade too, but I get always an exception that means that my AzureStorageAccount connectionstring is null. I did not found any possibility to configure the connectionstring. How did you configure the AzureStorageAccount connectionstring?
SeboStone
ok, I found a stupid magic in this post: https://github.com/Azure/azure-webjobs-sdk/issues/1963#issue-370375542
Nicholas Mitchell
Thanks for writing this, but having screwed around with this latest version of Web Jobs for a couple of days, hitting bug after bug with no documentation, and being completely new to Web Jobs, I’m sticking with version two. Updating the Web Jobs project template makes everything break! I tried working with V1 of Azure Functions but there were so many package conflicts that I moved on to Version 2, but the new framework it’s based on (.NET Core or Standard, or whatever it is) wouldn’t work with my .NET web app code. The whole thing has just been a big dumpster fire of pain. I really can’t believe how unusable and undocumented these products are when ASP.NET and Azure have been so good in the past.
So, one thing I can’t for the life of me figure out, if, as you said, “SDK 3.0 brings WebJobs much closer to ASP.NET Core”, is it still the old .NET? Or is it .NET Core, or Standard? And assuming they fix the usability and documentation problems, will V3 work with old .NET Framework code?
To submit comments, go to GitHub Discussions.