New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Visual Studio] How do perform dependency injection? #299

Open
RPM1984 opened this Issue May 18, 2017 · 38 comments

Comments

Projects
None yet
@RPM1984

RPM1984 commented May 18, 2017

Hi,

What's the recommended way to do DI in the new Azure Functions model?

I've seen mentions of the Service Locator pattern.

Any recommended samples/packages/code we should follow?

Is this the correct way? (not sure if still relevant)

Thanks

@slideep

This comment has been minimized.

slideep commented May 19, 2017

Excellent question, I've been tinkering with this also. I'm using a sortish of an Service Locator anti pattern which feels dirty, where say, ServiceConfigurator (the locator) class has static access to service objects configured into ServiceCollection and those are accessed via IServiceProvider contained services lazily after the provider has built up itself. ConfigurationBuilder builds up the app's configuration from variety of sources etc. Pretty much standard stuff where you build up the container. It does it's job for now but a proper 'bring your container' type of solution would be better if function app platform provides it. Hopefully you get the rough idea. Good luck! Pseudo snippets below..

private void ConfigureServices(IServiceCollection services)

ServiceProvider = services.BuildServiceProvider();

public static ISomeService<SomeContext> SomeService =>
    Configurator.Value.ServiceProvider.GetService<ISomeService<SomeContext>>();

@lindydonna lindydonna added the feature label May 23, 2017

@mack0196

This comment has been minimized.

mack0196 commented Aug 16, 2017

Is there a path for azure function projects to use dependency injection similar to the way webjobs can with IJobActivator?

@imsam67

This comment has been minimized.

imsam67 commented Aug 28, 2017

This is an important feature. Please move this up in your priorities list! Thank you!

@BorisWilhelms

This comment has been minimized.

BorisWilhelms commented Oct 10, 2017

There is actually a quite nice way to add dependency injection to your functions: https://blog.wille-zone.de/post/azure-functions-dependency-injection/

@RizwanSharp

This comment has been minimized.

RizwanSharp commented Oct 11, 2017

@BorisWilhelms Very cool, Any idea how it is going to work without Attribute i.e using function.json?

Thank You.

@RPM1984

This comment has been minimized.

RPM1984 commented Oct 11, 2017

@BorisWilhems yeah that's really nice! I'm surprised Microsoft hadn't issued any official advice/code/packages to cater for this, seems a very common requirement.

@lindydonna hi :) question - any movement on this internally? What's the current recommendation from Azure? Thanks!

@BorisWilhelms

This comment has been minimized.

BorisWilhelms commented Oct 11, 2017

@RizwanSharp Unfortunately I don't know how to make this work without attributes.

I have created a follow up post, with support for scoped services! https://blog.wille-zone.de/post/azure-functions-proper-dependency-injection/

@imsam67

This comment has been minimized.

imsam67 commented Nov 18, 2017

Any idea on when DI would be supported in Azure Functions out of the box?

@alexkarcher-msft

This comment has been minimized.

Member

alexkarcher-msft commented Jan 8, 2018

Linking this issue to UserVoice. DI is one of our top requested features there.

@bahagiga

This comment has been minimized.

bahagiga commented Jan 10, 2018

Is there an ETA? The UserVoice link requires login/signup which doesn't work for me :/

@bahagiga

This comment has been minimized.

bahagiga commented Jan 10, 2018

@thomasddn cheers buddy 👍

@yuka1984

This comment has been minimized.

yuka1984 commented Jan 28, 2018

I thought about an idea to do injection setting by function.
https://medium.com/@yuka1984/azure-functions-dependency-injection-with-injection-config-87fe5762c895

@MV10

This comment has been minimized.

MV10 commented Feb 2, 2018

Over the past two days I've enjoyed working with the code generously shared by @BorisWilhelms and @yuka1984. I was in dire need of a Functions DI solution, and was able to adapt their ideas into a reusable library. I also refactored a few other areas and tweaked the demo code, then threw together a blog post about the results.

https://mcguirev10.com/2018/02/01/reusable-dependency-injection-azure-function.html

Although I'm going to use the attribute approach they came up with, longer-term I think @mack0196 was on the right track thinking about IJobActivator. Towards the end of my blog post I float some very early thoughts about that which I hope to return to later when I have time to think through the call sequences.

@imsam67

This comment has been minimized.

imsam67 commented Feb 2, 2018

@MV10 Thank you! Also, thanks for the detailed article sharing not just your solutions but the thought processes you went through!

@MV10

This comment has been minimized.

MV10 commented Feb 4, 2018

This is turning out to be a very easy approach to live with. I'm finding my individual Function projects are focused enough that I need only create one reusable service registration class and method per project, and each Function class just references that -- service registration is three-lines of cut-and-paste. Here's a function for importing XML uploaded to blob storage. An upload trigger posts a queue message after validating the file (size, sender, etc) and this does the parsing. Very fast and clean coding this way.

public static class ParseUpload
{
    [FunctionName("ParseUpload")]
    public static async Task Run(
        [ServiceBusTrigger("xml-uploaded", AccessRights.Listen)]XmlUploaded message,
        [Inject] IBlobFileIO blobIO,
        [Inject] IXmlParser parser,
        [Inject] IMessageSenderCache sendercache,
        [Inject] ILog dbLog,
        TraceWriter log)
    {
        // processing code omitted
    }

    [FunctionName("RegisterServices")]
    public static void Reg([RegisterServicesTrigger] IServiceCollection services)
        => RegisterServices.Register(services);
}
@mennolaan

This comment has been minimized.

mennolaan commented Feb 12, 2018

Seeying people enhancing previous work by others always make me smile and appreciate github, awesome work guys!

@MV10

This comment has been minimized.

MV10 commented Feb 12, 2018

Cross-referencing another DI thread:
Azure/azure-functions-host#1579

@MV10

This comment has been minimized.

MV10 commented Feb 15, 2018

Quick note to anyone using or working from the stuff I posted a couple of weeks ago:

Functions SDK 1.0.8 adds a requirement that Function names be unique across the entire app, rather than just at class-scope (edit: that was my assumption, it was wrong), which means the convenience of having a default Function name to register your services within each class no longer works.

The good news is that if all your classes use the same services, they can all share a single reg function (that worked with 1.0.7 ... it just didn't occur to me to assume class scope would be violated/ignored).

@MV10

This comment has been minimized.

MV10 commented Apr 3, 2018

Here's a very simple stopgap solution for V2 Functions. This one is more about short-term convenience (getting DI working in libraries used by my Functions) than "pure" DI since Microsoft is working hard to add support for true DI in V2 Functions (as of runtime v3.0, as far as I can tell).

https://github.com/MV10/Azure.FunctionsV2.Service.Locator

@daulet

This comment has been minimized.

daulet commented May 13, 2018

I've created a custom input binding that you can reference and start injecting without extra boilerplate. It is as simple to use as:

public static IActionResult Run(
    [HttpTrigger("GET")] HttpRequest request,
    [Inject] IStorageAccess storageAccess)
{
    ...

Bonus: it also injects Microsoft.Extensions.Logging.ILogger to your dependencies. More details: https://github.com/daulet/Indigo.Functions#dependency-injection

@tsahi

This comment has been minimized.

tsahi commented May 14, 2018

Someone already did something like that: https://www.nuget.org/packages/AzureFunctions.Autofac having it hard-coded with Autofac (not my favorite) is OK for me, since I already have a dependency on it from Microsoft.Bot.Builder.

@BorisWilhelms

This comment has been minimized.

BorisWilhelms commented May 14, 2018

I have refactored my example and moved all DI code into its own project. With this solution there is no need for a "Configuration/Registration Function" :-) Guess I will create a proper nuget package :D

https://github.com/BorisWilhelms/azure-function-dependency-injection

@alexAlchemy

This comment has been minimized.

alexAlchemy commented Jun 22, 2018

Anybody know when the 3.0 release of Azure functions is due? Lack of properly supported DI is a barrier to adoption.

@ekzarov

This comment has been minimized.

ekzarov commented Aug 7, 2018

Any changes or news about that? Still not clear how to get settings from appsettings and inject them into function.

@akash3456

This comment has been minimized.

akash3456 commented Aug 7, 2018

@pseabury

This comment has been minimized.

pseabury commented Aug 9, 2018

The user voice topic indicates that we'll get granular updates here about DI, but there are no official updates as far as I can tell. Like the many others that have asked, can we get an update? This is important to us.

@imsam67

This comment has been minimized.

imsam67 commented Aug 9, 2018

Here's the response I got on Twitter from Fabio Cavalcante: https://twitter.com/codesapien/status/1025915051016744960

@pseabury

This comment has been minimized.

pseabury commented Aug 9, 2018

Thanks @imsam67 . Not was I was hoping for, but appreciate the info! I wonder if that means all the way out at 3.0 or in the plans for 2.x after GA...

@imsam67

This comment has been minimized.

imsam67 commented Aug 9, 2018

@pseabury If I take what Fabio is saying at its face value, it's certainly not 3.x but more like 2.1. Let's hope I'm right!

@MisinformedDNA

This comment has been minimized.

MisinformedDNA commented Aug 16, 2018

@ekzarov @akash3456 @Porkechebure

Still not clear how to get settings from appsettings and inject them into function.

For those who need appsettings, but not necessarily DI, you can call the following code in each function:

var config = new ConfigurationBuilder()
    .SetBasePath(context.FunctionAppDirectory)
    .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
    .AddEnvironmentVariables()
    .Build();
var setting1 = config["Setting1"];

You can read more about this here: https://blog.jongallant.com/2018/01/azure-function-config/

@ekzarov

This comment has been minimized.

ekzarov commented Aug 16, 2018

I ended up with the following solution:

What worries me here is that I don`t use appsettings.{env}.json, like I do in Azure web app and in integration tests in VSTS. Is it possible at the moment?

Thank you for you assistance!

@daulet

This comment has been minimized.

daulet commented Aug 17, 2018

@ekzarov sounds like you are looking for injecting a parameter read from application settings, not necessarily DI - there is a binding for that too, see this example: https://github.com/daulet/Indigo.Functions/blob/master/sample/ConfigurationFunctionSample/ConfigFunction.cs

@ekzarov

This comment has been minimized.

ekzarov commented Aug 19, 2018

@daulet Thank your for you answer, but:

I want to have appsettings.json (for local/DEBUG), appsettings.Development.json for dev enviroment in Azure WebJobs/Functions, appsettings.Stage.json for stage enviroment in Azure WebJobs/Functions, etc...
How Azure settings can help me here? In case of ASP.NET CORE I just need to specify ONE settings which is ASPNETCORE_ENVIRONMENT and specific config file will be used. So my question can I do the same with Azure functions/web jobs also? And if yes where I can find some documentation/sources on this topic?

Here is how I do in web app:

var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
#if !DEBUG
builder.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
#endif
builder.AddEnvironmentVariables();
Configuration = builder.Build();

PS: Sorry if my posts looks stupid, but there is really lack of info on this topic. Thank you.

@MisinformedDNA

This comment has been minimized.

MisinformedDNA commented Aug 19, 2018

@ekzarov I haven't tried this, but try copying the launchsettings.json file over and then try to get the value for ASPNET_ENVIRONMENT and see if it comes up. However, to use that variable to load the appsettings.[environment].json file, you may need to build the configuration twice.

Or you could define ASPNET_ENVIRONMENT in appsettings.json, build that configuration and then build a configuration that includes the environment specific appsettings files.

On my phone so hard to be detailed...

@mjyeaney

This comment has been minimized.

mjyeaney commented Oct 4, 2018

Just as another data point, a pattern I typically use in Functions is to leverage a static Lazy<T> for my main workflow. This essentially means all my "real" Function code is in an external class (which is very nice from a testing point of view). Basic code pattern looks like this:

private static Lazy<MyRealWork> workflow = new Lazy<MyRealWork>(() =>
{
     var dep1 = new SomeDependency();
     var dep2 = new AnotherDependency();
     return new MyRealWork(dep1, dep2);
});

[FunctionName("FooBarBaz")]
public static void Run([EventGridTrigger] EventGridEvent eventData, TraceWriter log)
{
     // This thread-safe by default, and gives is an instance of our pipeline
     var pipeline = workflow.Value;
     // Do real work...
     pipeline.DoWork(eventData);
}

Admittedly, this means you wiring up your dependency graph by hand - which would be nice to avoid - but the final code is still pretty reasonable. Looking forward to improvements here as well.

@VictorioBerra

This comment has been minimized.

@tsahi

This comment has been minimized.

tsahi commented Nov 13, 2018

When this was announced at Ignite last September, they said they won't support 3rd party DI libraries. As developers often have their own preference for one DI library or another, for various reasons, please make it possible to plug your preferred DI library to use as a container, just like ASP.NET MVC.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment