In a previous article we discovered in a simple scenario how to use Unity in a Microsoft Azure WebJob by creating a custom IJobActivator.
Continuing this article, today we will answer the following question:
How can I get a dependency scope only for the lifetime of my function?
This question might come up for example in a scenario where you need to reach a database in a function. In this kind of scenario you want to create a DbContext, query the database, close the connection, dispose all the resources properly, same as you would do in a web application with an HTTP request. Why? Because you don't want to end up creating a DbContext per job not knowing when everything will be disposed, you want to have the control on the dependencies lifetime.
Creation
Let's continue with the source code created in the previous article by creating the following interface:
using System; namespace AzureWebJobs.JobActivatorUnity.Dependencies { public interface IJobActivatorDependencyResolver : IDisposable { IJobActivatorDependencyScope BeginScope(); } }
Now we will create the Unity implementation of it:
using System; using AzureWebJobs.JobActivatorUnity.Dependencies; using Microsoft.Practices.Unity; namespace AzureWebJobs.JobActivatorUnity.Unity { public class UnityJobActivatorHierarchicalDependencyResolver : IJobActivatorDependencyResolver { private readonly IUnityContainer container; public UnityJobActivatorHierarchicalDependencyResolver(IUnityContainer container) { if (container == null) throw new ArgumentNullException("container"); this.container = container; } public IJobActivatorDependencyScope BeginScope() { return new UnityJobActivatorHierarchicalDependencyScope(this.container); } public void Dispose() { this.container.Dispose(); } private sealed class UnityJobActivatorHierarchicalDependencyScope : IJobActivatorDependencyScope { private readonly IUnityContainer container; public UnityJobActivatorHierarchicalDependencyScope(IUnityContainer parentContainer) { this.container = parentContainer.CreateChildContainer(); } public T CreateInstance<T>() { return this.container.Resolve<T>(); } public void Dispose() { this.container.Dispose(); } } } }
Thanks to this implementation every time a new scope is created a specific child container is created.
Example of use
We have created a JobActivatorDependencyResolver and we will learn how to use it.
First we will register it in the Unity container along with a disposable service:
using System; using AzureWebJobs.JobActivatorUnity.Contracts; using AzureWebJobs.JobActivatorUnity.Dependencies; 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<IUnitOfWork, DisposableService>(new HierarchicalLifetimeManager()); } } }
You can notice two elements here:
In our job function we can now use the IJobActivatorDependencyResolver:
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; private readonly INumberService numberService; public Functions(IJobActivatorDependencyResolver jobActivatorDependencyResolver, INumberService numberService) { if (jobActivatorDependencyResolver == null) throw new ArgumentNullException("jobActivatorDependencyResolver"); if (numberService == null) throw new ArgumentNullException("numberService"); this.jobActivatorDependencyResolver = jobActivatorDependencyResolver; this.numberService = numberService; } // 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) { log.WriteLine("New random number {0} from shared number service for message: {1}", this.numberService.GetRandomNumber(), message); using (var scope = this.jobActivatorDependencyResolver.BeginScope()) { Console.WriteLine("Beginning scope work..."); log.WriteLine("New random number {0} from scoped number service for message: {1}", scope.CreateInstance<INumberService>().GetRandomNumber(), message); await scope.CreateInstance<IUnitOfWork>().DoWork(ct, message); Console.WriteLine("Finishing scope work..."); } } } }
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 scope work...
DisposableService doing work...
Finishing scope work...
DisposableService disposing...
Executed: 'Functions.ProcessQueueMessage' (Succeeded)
Right after the scope work is done, the disposable service is disposed.
Summary
We have seen how to create a Dependency Scope in a Microsoft Azure WebJob function in a more advanced scenario which completes the previous article.
Now in the scenario we just covered there is place for improvement. Instead of calling a CreateInstance<T>() every time we need an object, we could simplify it by creating job handlers.
This will be the subject of the last article in the series about Dependency Injection in Azure WebJobs, stay tuned!
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.