Skip to content
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 · 47 comments
Open

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

RPM1984 opened this issue May 18, 2017 · 47 comments
Assignees
Labels
Milestone

Comments

@RPM1984
Copy link

@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
Copy link

@slideep 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>>();

@mack0196
Copy link

@mack0196 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
Copy link

@imsam67 imsam67 commented Aug 28, 2017

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

@BorisWilhelms
Copy link

@BorisWilhelms 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
Copy link

@RizwanSharp 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
Copy link
Author

@RPM1984 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
Copy link

@BorisWilhelms 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
Copy link

@imsam67 imsam67 commented Nov 18, 2017

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

@alexkarcher-msft
Copy link
Member

@alexkarcher-msft alexkarcher-msft commented Jan 8, 2018

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

@baharedankoyuncu
Copy link

@baharedankoyuncu baharedankoyuncu commented Jan 10, 2018

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

@thomasddn
Copy link

@thomasddn thomasddn commented Jan 10, 2018

@baharedankoyuncu
Copy link

@baharedankoyuncu baharedankoyuncu commented Jan 10, 2018

@thomasddn cheers buddy 👍

@yuka1984
Copy link

@yuka1984 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
Copy link

@MV10 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
Copy link

@imsam67 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
Copy link

@MV10 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
Copy link

@mennolaan mennolaan commented Feb 12, 2018

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

@MV10
Copy link

@MV10 MV10 commented Feb 12, 2018

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

@MV10
Copy link

@MV10 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
Copy link

@MV10 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
Copy link

@daulet 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
Copy link

@tsahi 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
Copy link

@BorisWilhelms 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
Copy link

@alexAlchemy 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
Copy link

@ekzarov 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
Copy link

@akash3456 akash3456 commented Aug 7, 2018

@pseabury
Copy link

@pseabury 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
Copy link

@imsam67 imsam67 commented Aug 9, 2018

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

@pseabury
Copy link

@pseabury 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
Copy link

@imsam67 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
Copy link

@MisinformedDNA 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
Copy link

@ekzarov 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
Copy link

@daulet 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
Copy link

@ekzarov 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
Copy link

@MisinformedDNA 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
Copy link

@mjyeaney 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
Copy link

@VictorioBerra VictorioBerra commented Nov 12, 2018

@tsahi
Copy link

@tsahi 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.

@jstallm
Copy link

@jstallm jstallm commented Jan 15, 2019

I still dont see a simple strait forward way to inject services, configuration, etc. ASP.NET Core DI is very simple to setup. How can we see how exactly Microsoft team is going to handle this need?

@PureKrome
Copy link

@PureKrome PureKrome commented Jan 15, 2019

@jstallm I was under the impression that it will be using the same DI that comes with .NET Core / ASP.NET Core. The Azure Functions team posted a video in December talking about various stuff - including this epic issue/thread and I think they said it was coming start of 2019 ... so it's on their radar and my assumption was that it would be the same DI which we're used to using in the rest of .NET Core.

My assumptions only.

@gdodd1977
Copy link

@gdodd1977 gdodd1977 commented Jan 23, 2019

Is there any new news on out of the box DI functionality for Functions v2?

@ranouf
Copy link

@ranouf ranouf commented Feb 10, 2019

Interested to know too.

@ColbyTresness ColbyTresness added this to the Visual Studio Triage milestone Feb 15, 2019
@ColbyTresness ColbyTresness self-assigned this Feb 15, 2019
@StefanSchoof
Copy link

@StefanSchoof StefanSchoof commented Feb 20, 2019

I can up with a poor man´s IoC Solution:

    public class IoTFunction
    {
        public RegistryManager RegistryManager { get; }

        public IoTFunction(RegistryManager registryManager)
        {
            RegistryManager = registryManager;
        }

        [FunctionName("IoTFunction")]
        [return: CosmosDB("xxx", "items" ConnectionStringSetting = "CosmosDBConnection")]
        public static Task<dynamic> Run(
            [IoTHubTrigger("messages/events", Connection = "IotHubEndpoint")]EventData message,
            ILogger log)
        {
            var ioTHubConnectionString = Environment.GetEnvironmentVariable(
                "IotHubConnection",
                EnvironmentVariableTarget.Process);
            var function = new IoTFunction(RegistryManager.CreateFromConnectionString(ioTHubConnectionString));
            return function.Main(message, log);
        }

        public async Task<dynamic> Main(EventData message, ILogger log)
        {
           # Do the work
        }
    }
}

In my Unit Test I can call the constructor with the mocked object and test the imported code in the Main method.

@ColbyTresness ColbyTresness removed this from the Visual Studio Triage milestone Feb 26, 2019
@ColbyTresness ColbyTresness added this to the Triaged milestone Feb 26, 2019
@jeffhollan
Copy link
Member

@jeffhollan jeffhollan commented Feb 27, 2019

Adding a link to where this work is being tracked. I don't want to close this issue just because so much good history, but work is being tracked here:

Azure/azure-functions-host#3736

@PureKrome
Copy link

@PureKrome PureKrome commented May 10, 2019

@Planche95
Copy link

@Planche95 Planche95 commented Aug 19, 2019

Anyone have similar issue? AddScoped behaves like singleton
#1302

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet