Azure Functions, Neo4j and Dependency Injection

By Charlotte

TL;DR; – It’s on GitHub: https://github.com/cskardon/Neo4jDriverWithAzureFunctionsDI

You, just now

I know what you’re thinking, that is an exciting headline, and you’re right! Back in May… May 2018 (!!!) I wrote a blog post about using the Neo4j Driver with Azure Functions – and well, things have moved on a bit since then. Largely the code to actually do the work is still OK, but there are some issues, largely around performance.

The official (and indeed my community) client for Neo4j (Neo4j.Driver/Neo4jClient) suffer from the same thing a lot of objects in C# (and most Object Oriented (OO) languages do) – that instantiating a new object is expensive from a CPU cycle point of view.

In the case of the official driver, it creates a Session Pool which if you’re only going to use once, is a lot of overhead. The community driver as it uses the official one under the hood for Bolt has the same issue.

Azure Functions are functions (code) triggered by an event, this could be HTTP (which it will be in this case) or from any of the other things Azure provides – IOT Service Bus etc. As part of this, they have no state, so in my previous example, a new Driver is created, the code is executed and it’s closed down.

For rare events, that’s no problem.

For frequent events. That’s a problem.

Step in Dependency Injection – we’ll create the Driver once, and use it as many times as we need.

Startup

First, we need a startup class, so let’s add that:

public class Startup : FunctionsStartup
{
}

I’ve called it Startup but you can call it whatever you want, as long as it derives from the FunctionsStartup class, we’re all good. Now, we need to add an override:

public override void Configure(IFunctionsHostBuilder builder) {}

And within that method, we’re going to construct and add a Singleton instance of our Driver:

builder.Services.AddSingleton<IDriver>((s) =>
{
     var driver = GraphDatabase.Driver("neo4j://localhost:7687", AuthTokens.Basic("neo4j", "neo"));
     return driver;
});

Obviously, you should use your own URI and user/password combinations – mine will likely not work for you – and if they do – then you should definitely change your password!

So. We add a Singleton to the ‘Builder’ Services, of type IDriver – I could have done it in one line, but I prefer the clarity of the two lines.

Anyhews – obviously, if you have special logging, or other things for which DI would be useful, add them all to this method, I’m going with small and simple, but you get the idea!

We now have a class, but we need to add one final thing – a notification to the Function that this is the startup class, to do that we use an assembly directive:

[assembly: FunctionsStartup(typeof(Neo4jDriver.AzureFunction.DependencyInjection.Startup))]

The typeof needs to be the fully defined name of your class, so put it outside of your namespace declarations!

Function

The default function that VS will create for you is a static method in a static class, but that’s not going to fly if we want to be using DI, so our first step is to change the class and method definitions to not be static – which is as simple as deleting the static keyword.

Next! We need to inject the IDriver instance, now, we’re using the default DI and that means that we can just add the types we want to our constructor, and we’re all good, so let’s do that:

private readonly IDriver _driver;

public DriverWithDI(IDriver driver)
{
    _driver = driver;
}

NB. I’m storing the IDriver into a readonly member, so I can use that from within my function.

var session = _driver.AsyncSession();
var count = await session.ReadTransactionAsync(async tx =>
{
    var res = await tx.RunAsync("MATCH (n) RETURN COUNT(n)");
    var record = await res.SingleAsync();
    return record["COUNT(n)"].As<int>();
});

var responseMessage = $"There are {count} nodes in the database";
return new OkObjectResult(responseMessage);

So, the only things to really note:

  1. I get the AsyncSession from the _driver (which we assigned in the constructor)
  2. I use a ReadTransactionAsync to do the work (which is Cluster safe)
  3. I’m just returning the count of the nodes in the DB, so super quick 🙂

And. Well. That’s kind of it really, the code is available on GitHub (obvs). So have a play, but this should get you going with Neo4j and Azure Functions!