Friday, November 30, 2018

Scoped DI Provider for Azure Function

The azure function does not provide an in built support for dependency injection (at least at the time of writing this article). There is however an user voice ticket marked as started at this point.

Currently, there is a good nuget package available using which you could bring DI support with little effort as mentioned in its repo home page. The author of this package Boris Wilhelms, also wrote a blog post mentioning the working of this package if you would like to understand how it works.

Anyways, I was however looking for even simplified workaround until Microsoft provides native support.

So, here is the approach that I followed.

Created following wrapper class for DI:

public static class FunctionScopedDIProvider
{
    private static IServiceProvider serviceProvider;
    private static readonly object locker = new object();

    public static async Task Using(Func<IServiceProvider, Task> action)
    {
        if (serviceProvider == null)
        {
            lock (locker)
            {
                if (serviceProvider == null)
                {
                    serviceProvider = BuildServiceProvider();
                }
            }
        }

        var scopeFactory = serviceProvider.GetService<IServiceScopeFactory>();
        using (var scope = scopeFactory.CreateScope())
        {
            await action(scope.ServiceProvider);
        }
    }

    private static IServiceProvider BuildServiceProvider()
    {
        var services = new ServiceCollection();
        
        // TODO: Do your registrations here.
        // Ex: services.AddScoped<Imyservice, Myservice>();
        
        return services.BuildServiceProvider();
    }
}

Which can be used like this:

[FunctionName("Function1")]
public static async Task Run([QueueTrigger("myqueue-items", Connection = "")]string myQueueItem, ILogger log)
{
    await FunctionScopedDIProvider.Using(async provider =>
    {
        // The provider here would work as expeceted including scoped lifetime.
        // Ex: var myService = provider.GetService<IMyservice>();

        // TODO: You function logic goes here. 

        // A mock task completion as we don't have a real awaitable task here.
        await Task.CompletedTask;
    });

    log.LogInformation($"C# Queue trigger function processed: {myQueueItem}");
}

Note: The Using function of FunctionScopedDIProvider above not necessarily need to be async and so is the parameter. It can simply be an action delegate instead of function delegate.

This is how the non async version signature would look like.

public static void Using(Action<IServiceprovider> action)

Are you wondering why we need to do this, when we can easily get the DI provider by calling BuildServiceProvider method of ServiceCollectio? Well, its because singleton and scoped object liefetime support of the DI.

In other words, if you create new ServiceProvider each time, you won't get singleton behavior and if you reuse the ServiceProvider then you won't get scoped behavior (The reuse can be done by keeping it in a static variable).

So the solution that I come up with is by creating a static ServiceProvider  in a thread-safe manner and creating new scope each time with the help of in-built IServiceScopeFactory.

We can actually keep this stuff in the Run function's body itself but that's a cluttering code and not re-usable as well. So, created a nice wrapper with delegate using pattern (refer the second point in the article, titled - Measuring various function/method execution using StopWatch).

Enough talking, here is the full code. Enjoy your day!

Important: This code doesn't work if you would like to do the DI for function entry itself. For that you should use the solution provided by Boris Wilhelms which I mentioned in the beginning of this blog.

1 comment:

  1. Big data is a term that describes the large volume of data – both structured and unstructured – that inundates a business on a day-to-day basis. IEEE Projects for CSE in Big Data But it’s not the amount of data that’s important. Final Year Project Centers in Chennai It’s what organizations do with the data that matters. Big data can be analyzed for insights that lead to better decisions and strategic business moves.

    Java has already made serious inroads as an integrated technology stack for building user-facing applications. Java Training in Chennai the authors explore the idea of using Java in Big Data platforms.
    Specifically, various tasks are geared around preparing data for further analysis and visualization. Java Training in Chennai

    ReplyDelete