In a previous article we discovered in an advanced scenario how to implement Dependency Injection and Dependency Scope per job in Azure WebJobs with Unity.
Today in this last article in the series of articles about Microsoft Azure WebJob, we will discover how to process a function using a Job Handler.
Creation
Let's continue with the source code created in the previous article by creating the following interface:
using System.IO; using System.Threading; using System.Threading.Tasks; namespace AzureWebJobs.JobActivatorUnity.Contracts { public interface IQueueMessageJob { Task Process(CancellationToken ct, string message, TextWriter log); } }
Now we will create the implementation of it:
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using AzureWebJobs.JobActivatorUnity.Contracts; namespace AzureWebJobs.JobActivatorUnity.Handlers { public sealed class QueueMessageJobHandler : IQueueMessageJob { private readonly INumberService numberService; private readonly IUnitOfWork unitOfWork; public QueueMessageJobHandler(INumberService numberService, IUnitOfWork unitOfWork) { if (numberService == null) throw new ArgumentNullException(nameof(numberService)); if (unitOfWork == null) throw new ArgumentNullException(nameof(unitOfWork)); this.numberService = numberService; this.unitOfWork = unitOfWork; } public async Task Process(CancellationToken ct, string message, TextWriter log) { Console.WriteLine("Beginning QueueMessageJobHandler work..."); log.WriteLine("New random number {0} from number service for message: {1}", this.numberService.GetRandomNumber(), message); await this.unitOfWork.DoWork(ct, message); Console.WriteLine("Finishing QueueMessageJobHandler work..."); } } }
As you can notice we moved the code previously in the function to the Job Handler process method.
Example of use
We have created the QueueMessageJobHandler and we will learn how to use it.
First we will register it in the Unity container:
using System; using AzureWebJobs.JobActivatorUnity.Contracts; using AzureWebJobs.JobActivatorUnity.Dependencies; using AzureWebJobs.JobActivatorUnity.Handlers; using AzureWebJobs.JobActivatorUnity.Services; using AzureWebJobs.JobActivatorUnity.Unity; using Microsoft.Practices.Unity; namespace AzureWebJobs.JobActivatorUnity { public class UnityConfig { #region Unity Container private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() => { var container = new UnityContainer(); RegisterTypes(container); return container; }); /// <summary> /// Gets the configured Unity container. /// </summary> public static IUnityContainer GetConfiguredContainer() { return container.Value; } #endregion /// <summary>Registers the type mappings with the Unity container.</summary> /// <param name="container">The unity container to configure.</param> public static void RegisterTypes(IUnityContainer container) { container.RegisterType<IJobActivatorDependencyResolver, UnityJobActivatorHierarchicalDependencyResolver>(new ContainerControlledLifetimeManager(), new InjectionConstructor(container.CreateChildContainer())); container.RegisterType<INumberService, NumberService>(new ContainerControlledLifetimeManager()); container.RegisterType<IQueueMessageJob, QueueMessageJobHandler>(new HierarchicalLifetimeManager()); container.RegisterType<IUnitOfWork, DisposableService>(new HierarchicalLifetimeManager()); } } }
Here we register the Job Handler along with the other services.
In our job function we can now use the IQueueMessageJob:
using System; using System.IO; using System.Threading; using System.Threading.Tasks; using AzureWebJobs.JobActivatorUnity.Contracts; using AzureWebJobs.JobActivatorUnity.Dependencies; using Microsoft.Azure.WebJobs; namespace AzureWebJobs.JobActivatorUnity { public class Functions { private readonly IJobActivatorDependencyResolver jobActivatorDependencyResolver; public Functions(IJobActivatorDependencyResolver jobActivatorDependencyResolver) { if (jobActivatorDependencyResolver == null) throw new ArgumentNullException(nameof(jobActivatorDependencyResolver)); this.jobActivatorDependencyResolver = jobActivatorDependencyResolver; } // This function will get triggered/executed when a new message is written // on an Azure Queue called queue. public async Task ProcessQueueMessage([QueueTrigger("queue")] string message, TextWriter log, CancellationToken ct) { using (var scope = this.jobActivatorDependencyResolver.BeginScope()) { await scope.CreateInstance<IQueueMessageJob>().Process(ct, message, log); } } } }
We can pay attention to several things here:
To go further
If you test the source code, the console output will be the following when the function is triggered:
Executing: 'Functions.ProcessQueueMessage' - Reason: 'New queue message detected on 'queue'.'
Beginning QueueMessageJobHandler work...
DisposableService doing work...
Finishing QueueMessageJobHandler work...
DisposableService disposing...
Executed: 'Functions.ProcessQueueMessage' (Succeeded)
Right after the Job Handler work is done, the disposable service is disposed.
Summary
We have seen how to create a Job Handler for a Microsoft Azure WebJob function which completes the series of articles about Microsoft Azure WebJob and Dependency Injection.
You can download the example solution here:
Or
Browse the GitHub repository
(Note that the project uses Microsoft.Azure.WebJobs version 1.1.2)
Please feel free to comment or contact me if you have any question about this article.