Azure Functions are a relatively new kid on the block when it comes to the Microsoft Azure stack. I’ve previously written about them, and their limitations. One such limitation seems to be that they don’t lend themselves very well to using dependency injection. However, it is certainly not impossible to make them do so.
In this particular post, we’ll have a look at how you might use an IoC container (Unity in this case) in order to leverage DI inside an Azure function.
New Azure Functions Project
I’ve covered this before in previous posts, in Visual Studio, you can now create a new Azure Functions project:
That done, you should have a project that looks something like this:
As you can see, the elephant in the room here is there are no functions; let’s correct that:
Be sure to call your function something descriptive… like “Function1”. For the purposes of this post, it doesn’t matter what kind of function you create, but I’m going to create a “Generic Web Hook”.
Install Unity
The next step is to install Unity (at the time of writing):
Install-Package Unity -Version 5.5.6
Static Variables Inside Functions
It’s worth bearing mind that a static variable works the way you would expect, were the function a locally hosted process. That is, if you write a function such as this:
[FunctionName("Function1")]
public static object Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
System.Threading.Thread.Sleep(10000);
log.Info($"Index is {test}");
return req.CreateResponse(HttpStatusCode.OK, new
{
greeting = $"Hello {test++}!"
});
}
And access it from a web browser, or postman, or both as the same time, you’ll get incrementing numbers:
Whilst the values are shared across the instances, you can’t cause a conflict by updating something in one function while reading it in another (I tried pretty hard to cause this to break). What this means, then, is that we can store an IoC container that will maintain state across function calls. Obviously, this is not intended for persisting state, so you should assume your state could be lost at any time (as indeed it can).
Registering the Unity Container
One method of doing this is to use the Lazy object. This pretty much passed me by in .Net 4 (which is, apparently, when it came out). It basically provides a slightly neater way of doing this kind of thing:
private List<string> \_myList;
public List<string> MyList
{
get
{
if (\_myList == null)
{
\_myList = new List<string>();
}
return \_myList;
}
}
The “lazy” method would be:
public Lazy<List<string>> MyList = new Lazy<List<string>>(() =>
{
List<string> newList = new List<string>();
return newList;
});
With that in mind, we can do something like this:
public static class Function1
{
private static Lazy<IUnityContainer> \_container =
new Lazy<IUnityContainer>(() =>
{
IUnityContainer container = InitialiseUnityContainer();
return container;
});
InitialiseUnityContainer needs to return a new instance of the container:
public static IUnityContainer InitialiseUnityContainer()
{
UnityContainer container = new UnityContainer();
container.RegisterType<IMyClass1, MyClass1>();
container.RegisterType<IMyClass2, MyClass2>();
return container;
}
After that, you’ll need to resolve the parent dependency, then you can use standard constructor injection; for example, if MyClass1 orchestrates your functionality; you could use:
\_container.Value.Resolve<IMyClass1>().DoStuff();
In Practise
Let’s apply all of that to our Functions App. Here’s two new classes:
public interface IMyClass1
{
string GetOutput();
}
public interface IMyClass2
{
void AddString(List<string> strings);
}
public class MyClass1 : IMyClass1
{
private readonly IMyClass2 \_myClass2;
public MyClass1(IMyClass2 myClass2)
{
\_myClass2 = myClass2;
}
public string GetOutput()
{
List<string> teststrings = new List<string>();
for (int i = 0; i <= 10; i++)
{
\_myClass2.AddString(teststrings);
}
return string.Join(",", teststrings);
}
}
public class MyClass2 : IMyClass2
{
public void AddString(List<string> strings)
{
Thread.Sleep(1000);
strings.Add($"{DateTime.Now}");
}
}
And the calling code looks like this:
[FunctionName("Function1")]
public static object Run([HttpTrigger(WebHookType = "genericJson")]HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
string output = \_container.Value.Resolve<IMyClass1>().GetOutput();
return req.CreateResponse(HttpStatusCode.OK, new
{
output
});
}
Running it, we get an output that we might expect: