Azure Functions V2 now supports .net dependency injection
In order to achieve that you need to do the following code:
[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]
namespace MyNamespace
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton((s) => {
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
});
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
}
}
}
I want to change the default container from .net to "Lamar" DI framework.
On their documentation they have an example for a WebHost:
var builder = new WebHostBuilder();
builder
// Replaces the built in DI container
// with Lamar
.UseLamar()
// Normal ASP.Net Core bootstrapping
.UseUrls("http://localhost:5002")
.UseKestrel()
.UseStartup<Startup>();
builder.Start();
But I'm not able to change IFunctionsHostBuilder to use "UseLamar()" extension. Since this extends IWebHostBuilder. The only ways I was able to intercept the initialization of azure functions was Either with FunctionsStartup that configures IFunctionsHostBuilder or IWebJobsStartup that configures IWebJobsBuilder, but I don't find extensions for those kinds of builds on Lamar.
I've tried to check the existing extension to create a similar code but is not working because probably I need to create more stuff:
[assembly: FunctionsStartup(typeof(FunctionAppPrototype.Startup))]
namespace FunctionAppPrototype
{
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var container = new Container(x =>
{
x.AddTransient<IMyService, MyService>();
});
builder.Services.AddSingleton<IServiceProviderFactory<IServiceCollection>, LamarServiceProviderFactory>();
builder.Services.AddSingleton<IServiceProviderFactory<ServiceRegistry>, LamarServiceProviderFactory>();
}
}
}
After some research, I was able to find a solution using Autofac. I was not able to do it with Lamar it had no extension either for IFunctionsHostBuilder or IWebJobsBuilder.
Source Code: Binding extensions for dependency injection in Azure Function v2
Nuget: Willezone.Azure.WebJobs.Extensions.DependencyInjection
First, you need to intercept the startup of the function app by doing the following code:
[assembly: WebJobsStartup(typeof(AutoFacFunctionAppPrototype.WebJobsStartup))]
namespace AutoFacFunctionAppPrototype
{
public class WebJobsStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder) =>
builder.AddDependencyInjection<AutoFacServiceProviderBuilder>();
}
}
Then create the container and register the dependencies:
namespace AutoFacFunctionAppPrototype.Builders
{
public class AutoFacServiceProviderBuilder : IServiceProviderBuilder
{
private readonly IConfiguration configuration;
public AutoFacServiceProviderBuilder(IConfiguration configuration)
=> this.configuration = configuration;
public IServiceProvider Build()
{
var services = new ServiceCollection();
services.AddTransient<ITransientService, TransientService>();
services.AddScoped<IScopedService, ScopedService>();
var builder = new ContainerBuilder();
builder.RegisterType<SingletonService>().As<ISingletonService>().SingleInstance();
builder.Populate(services); // Populate is needed to have support for scopes.
return new AutofacServiceProvider(builder.Build());
}
}
}
Then you can use them on the function using the attribute [Inject]:
namespace AutoFacFunctionAppPrototype.Functions
{
public static class CounterFunction
{
[FunctionName("Counter")]
public static IActionResult Run(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
[Inject]ITransientService transientService,
[Inject]IScopedService scopedService,
[Inject]ISingletonService singletonService,
ILogger logger)
{
logger.LogInformation("C# HTTP trigger function processed a request.");
string result = String.Join(Environment.NewLine, new[] {
$"Transient: {transientService.GetCounter()}",
$"Scoped: {scopedService.GetCounter()}",
$"Singleton: {singletonService.GetCounter()}",
});
return new OkObjectResult(result);
}
}
}
Using this approach I was only able to inject parameters, was not able to do constructor or property injection, even though I was a non-static class.
Note: If in the future Autofac supports extension for IFunctionsHostBuilder, probably would be better to use that approach instead of IWebJobsStartup.