Search code examples
.net-coredependency-injectionazureservicebusazure-webjobsazure-servicebus-queues

Can't bind service implementation using DI inside function with ServiceBusTrigger parameter in .net core Azure Web Job


I have a .net core console application I am going to deploy as an Azure web job. The purpose of the job is to listen for new messages in an Azure Service Bus Queue. I have set the listener up using a function containing the [ServiceBusTrigger] Attribute. I built a dummy implementation which just reads the latest message from the queue - this works without issue - the message is passed correctly from the service bus queue to my function.

When I try to go to the next level and add an interface parameter to the function to be injected by DI I get an error.

Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException: 'Error indexing method 'Functions.ProcessMeasurementData'' InvalidOperationException: Cannot bind parameter 'service' to type IProcessMeasurementService. 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.).

This is my function. If I remove the parameter IProcessMeasurementService service it works with no issue running locally from Visual Studio 2019 (I haven't tried deploying to azure yet as a webjob), picking up new items as they are added to the Azure Service Bus Queue.

public class Functions
{
    public static async Task ProcessMeasurementData(
        [ServiceBusTrigger("process-measurement-data-queue", Connection = "AzureWebJobsServiceBus")] Message message,
    IProcessMeasurementService service)
    {
        try
        {

            var measurements = JsonConvert
                .DeserializeObject<List<CreateMeasurementInput>>
                    (Encoding.UTF8.GetString(message.Body));

            await service.DoStuff(measurements);

            // log.LogInformation(message.ContentType);
        }
        catch (Exception e)
        {

            throw;
        }
    }

I think I am registering the service correctly, like this:

            {
                // Register application services
                services.AddSingleton<IProcessMeasurementService, ProcessMeasurementService>();
            });

This is my main function in its entirety.

        static void Main(string[] args)
    {
        var builder = new HostBuilder();

        builder.ConfigureAppConfiguration((builder) =>
        {
            builder

                .AddJsonFile("appsettings.json", false, true)
                .AddEnvironmentVariables();
        });


        builder.ConfigureWebJobs(b =>
        {
            b.AddServiceBus(x =>
            {
                x.MessageHandlerOptions = new MessageHandlerOptions(ExceptionReceivedHandler)
                {
                    AutoComplete = false
                };
            });

            b.AddAzureStorageCoreServices();
        });

        builder.ConfigureServices(services =>
        {
            services.AddOptions();

            // Register application services
            services.AddSingleton<IProcessMeasurementService, ProcessMeasurementService>();
        });

        var host = builder.Build();

        using (host)
        {
            host.Run();
        }
    }

From googling it feels like the problem might be something to do with my nuget package version. I tried adding a file called "host.json" in case it was a known problem with azure function versions conflicting with extensions libraries. But it did nothing. I am not actually using AzureFunctions (serverless functions I mean) but I am clutching at straws at this stage.

Does anyone know what the problem is?

This is the host.json just in case this is the issue.

{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle",
    "version": "[1.*, 2.0.0)"
  }
}

Here're my nuget versions installed

<PackageReference Include="Microsoft.Azure.ServiceBus" Version="4.1.2" />
<PackageReference Include="Microsoft.Azure.WebJobs" Version="3.0.16" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.ServiceBus" Version="4.1.1" />
<PackageReference Include="Microsoft.Azure.WebJobs.Sources" Version="3.0.16" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.2.0" />

Solution

  • Change from static function method to an instance member and inject the service via constructor injection directly into the function class.

    public class Functions {
    
        private readonly  IProcessMeasurementService service;
    
        public Functions (IProcessMeasurementService service) {
            this.service = service;
        }
    
        public async Task ProcessMeasurementData(
            [ServiceBusTrigger("process-measurement-data-queue", Connection = "AzureWebJobsServiceBus")] Message message) 
        {
            try {
    
                var measurements = JsonConvert
                    .DeserializeObject<List<CreateMeasurementInput>>
                        (Encoding.UTF8.GetString(message.Body));
    
                await service.DoStuff(measurements);
    
                // log.LogInformation(message.ContentType);
            } catch (Exception e) {
                //...    
                throw;
            }
        }
    }