11
votes

I'm trying to create my own custom binding for Azure Functions. This work is based on 2 wiki articles concerning this feature: https://github.com/Azure/azure-webjobs-sdk/wiki/Creating-custom-input-and-output-bindings and https://github.com/Azure/WebJobsExtensionSamples

For a sample project, I'm referring to the Azure Functions/WebJobs binding extension sample project. This project is based on the .NET Framework 4.6.

I want my own custom binding to work with Azure Functions v2, so I'm targetting NetStandard2 in all of my projects.

In the sample project, I see the binding extension is getting loaded when starting the local emulator.

[3-1-2019 08:48:02] Loaded binding extension 'SampleExtensions' from 'referenced by: Method='FunctionApp.WriterFunction.Run', Parameter='sampleOutput'.'

However, I never see this line appearing when running my own binding extension.

What I've done is the following. First, I've created a new attribute.

[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue)]
[Binding]
public class MySimpleBindingAttribute : Attribute
{
    /// <summary>
    /// Path to the folder where a file should be written.
    /// </summary>
    [AutoResolve]
    public string Location { get; set; }
}

And a rather simple extension class

public class MySimpleBindingExtension : IExtensionConfigProvider
{
    public void Initialize(ExtensionConfigContext context)
    {
        var rule = context.AddBindingRule<MySimpleBindingAttribute>();
        rule.BindToInput<MySimpleModel>(BuildItemFromAttribute);
    }

    private MySimpleModel BuildItemFromAttribute(MySimpleBindingAttribute arg)
    {
        string content = default(string);
        if (File.Exists(arg.Location))
        {
            content = File.ReadAllText(arg.Location);
        }

        return new MySimpleModel
        {
            FullFilePath = arg.Location,
            Content = content
        };
    }
}

And of course, the model.

public class MySimpleModel
{
    public string FullFilePath { get; set; }
    public string Content { get; set; }
}

From what I've been reading on the wiki I think this should be enough to use it in my Azure Functions project. The Function looks like this.

[FunctionName("CustomBindingFunction")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "{name}")]
    HttpRequest req,
    string name,
    [MySimpleBinding(Location = "%filepath%\\{name}")]
    MySimpleModel simpleModel)
{
    return (ActionResult) new OkObjectResult(simpleModel.Content);
}

When running this in the emulator I'm seeing the following error & warning messages.

[3-1-2019 08:51:37] Error indexing method 'CustomBindingFunction.Run'

[3-1-2019 08:51:37] Microsoft.Azure.WebJobs.Host: Error indexing method 'CustomBindingFunction.Run'. Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'simpleModel' to type MySimpleModel. Make sure the parameter Type is supported by the binding. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

[3-1-2019 08:51:37] Function 'CustomBindingFunction.Run' failed indexing and will be disabled.

[3-1-2019 08:51:37] No job functions found. Try making your job classes and methods public. If you're using binding extensions (e.g. Azure Storage, ServiceBus, Timers, etc.) make sure you've called the registration method for the extension(s) in your startup code (e.g. builder.AddAzureStorage(), builder.AddServiceBus(), builder.AddTimers(), etc.).

Now, this message makes sense in a WebJob scenario, but for an Azure Function you con't have a place to set something up in the startup code. Invoking the Function throws the following message.

[3-1-2019 08:53:13] An unhandled host error has occurred.

[3-1-2019 08:53:13] Microsoft.Azure.WebJobs.Host: 'CustomBindingFunction' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?.

I've read something about adding an extensions.json file and use the AzureWebJobs_ExtensionsPath (for v1), but those don't appear do do anything (or I've done something wrong).

Any ideas on how to proceed?

For completeness sake, here are my current references to NuGet packages.

<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.3" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.24" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
1

1 Answers

7
votes

With some help and guidance of other examples I was able to figure out what needs to be added at the moment to get your custom bindings working.

My guess is this will change over time in upcoming releases of the runtime, but who knows...

First, add a small extension method in which you add your extension to the IWebJobsBuilder.

public static class MySimpleBindingExtension
{
    public static IWebJobsBuilder AddMySimpleBinding(this IWebJobsBuilder builder)
    {
        if (builder == null)
        {
            throw new ArgumentNullException(nameof(builder));
        }

        builder.AddExtension<MySimpleBinding>();
        return builder;
    }
}

Next is the IWebJobsStartup implementation. This will get picked up on startup by the runtime via reflection.

public class MySimpleBindingStartup : IWebJobsStartup
{
    public void Configure(IWebJobsBuilder builder)
    {
        builder.AddMySimpleBinding();
    }
}

One thing you must not forget is to add the the following to this class:

[assembly: WebJobsStartup(typeof(MySimpleBindingStartup))]

You'll notice this soon enough, because if you forget, the binding won't get picked up.

Hope this helps for everyone hitting the same issue I had. If you want to see the complete code, I've got a sample project in a GitHub repository available: https://github.com/Jandev/CustomBindings