We use dependency injection in our Azure Function (v2 on netstandard20) using parameter binding with IExtensionConfigProvider. After upgrading Microsoft.NET.Sdk.Functions from 1.0.13 to 1.0.19 (which forced an upgrade of Microsoft.Azure.Webjobs.Host to v3) this doesn't work anymore. I can't hit a breakpoint in my IExtensionConfigProvider.Initialize function any more. The same version of the Functions SDK works fine for a sample project with target framework net462, for which it uses Microsoft.Azure.WebJobs v2.
Here's the error it gives at runtime:
Error indexing method 'Function1.Run'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'customThing' to type CustomType. Make sure the parameter Type is supported by the binding.
And here's the code for the sample app:
public static class Function1
{
[FunctionName("ThisFunction")]
public static async Task Run(
[TimerTrigger("0 */1 * * * *")]TimerInfo timer,
[Inject(typeof(CustomType))] CustomType customThing,
ExecutionContext context)
{
Console.WriteLine(customThing.GetMessage());
}
}
public class CustomType
{
public string GetMessage() => "Hi";
}
[Binding]
[AttributeUsage(AttributeTargets.Parameter)]
public class InjectAttribute : Attribute
{
public Type Type { get; }
public InjectAttribute(Type type) => Type = type;
}
public class InjectConfiguration : IExtensionConfigProvider
{
private IServiceProvider _serviceProvider;
public void Initialize(ExtensionConfigContext context)
{
var services = new ServiceCollection();
services.AddSingleton<CustomType>();
_serviceProvider = services.BuildServiceProvider(true);
context
.AddBindingRule<InjectAttribute>()
.BindToInput<dynamic>(i => _serviceProvider.GetRequiredService(i.Type));
}
}
With the changes made in v3 to DI and the extension model to create a extension (an IExtensionConfigProvider implementation is an extension) now you first need to create a startup class, using [assembly:WebJobsStartup] assembly attribute and implementing IWebJobsStartup interface. In there you can add your own services to the builder via builder.Services and register your extension's config provider class:
[assembly: WebJobsStartup(typeof(WebJobsExtensionStartup ), "A Web Jobs Extension Sample")]
namespace ExtensionSample
{
public class WebJobsExtensionStartup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
//Don't need to create a new service collection just use the built-in one
builder.Services.AddSingleton<CustomType>();
//Registering an extension
builder.AddExtension<InjectConfiguration>();
}
}
}
Then in your IExtensionConfigProvider you can inject any dependencies via constructor injections, for example, binding, bindingproviders, or any other custom dependency. In your case you can just get a reference to the built-in IServiceProvider:
public class InjectConfiguration : IExtensionConfigProvider
{
private IServiceProvider _serviceProvider;
public InjectConfiguration(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public void Initialize(ExtensionConfigContext context)
{
context
.AddBindingRule<InjectAttribute>()
.BindToInput<dynamic>(i => _serviceProvider.GetRequiredService(i.Type));
}
}
To get the host to load the extension, it must be registered inside bin/extensions.json file, in JavaScript or Java functions via the func extensions install command. In C# the SDK 1.0.19 looks at build time for classes attributed with WebJobsStartup assembly attribute in the current function project or any dependency (ProjectReference or PackageReference) of the current project, and generates the corresponding extensions.json file.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With