3
votes

I am creating an Azure web job in .Net Core 2.1 (via a "console" app in Visual Studio). In this project, I have a static function that reads messages from a queue. Within this function, I need to use connection strings (from my configuration) to write to a database. This is my setup:

Program.cs

class Program
{
    static void Main(string[] args)
    {
        var builder = new HostBuilder();
        builder.ConfigureWebJobs(b =>
        {
            b.AddAzureStorageCoreServices();
            b.AddAzureStorage();
        });
        builder.ConfigureAppConfiguration((hostContext, config) =>
        {
            var conf = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
            config.AddConfiguration(conf);
            config.AddEnvironmentVariables();
        })
        builder.ConfigureLogging((context, b) =>
        {
            b.AddConsole();
        });
        var host = builder.Build();
        using (host)
        {
            host.Run();
        }
    }
}

Functions.cs

public class Functions
{
    public static void ProcessQueueMessage([QueueTrigger("myqueue")] string message, ILogger logger, IConfiguration configuration)
    {
        logger.LogInformation(message);
        logger.LogInformation(configuration.GetConnectionString("MyDatabase"));
    }
}

appsettings.json

{
  "ConnectionStrings": {
    "MyDatabase": "foo",
    "AzureWebJobsDashboard": "foo2",
    "AzureWebJobsStorage": "foo3"
  }
}

However, when I run this, I get the following error:

Error indexing method 'Functions.ProcessQueueMessage'

Cannot bind parameter 'configuration' to type IConfiguration. 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.).

I am very new to .Net Core, especially the DI pattern. And I believe that is the issue. I also see many examples of how to implement and use the configuration from within the Main function, but not from within a static helper function like this. How do I properly implement my configuration from within my static function?

1
Is this WebJobs or Azure Functions?DavidG
It is a WebJob.Matt Spinks
Not something I have a lot of experience with, but inside ConfigureWebJobs can you do b.Services.AddConfiguration() or even b.Services.AddSingleton<IConfiguration, Configuration>()?DavidG
Unfortunately that does not fix it. That's a good thought, though. I was thinking I needed something like that. But unfortunately I get the same error.Matt Spinks
Well make it not static!DavidG

1 Answers

1
votes

Consider changing the approach and not try to inject IConfiguration.

Create a class to hold your desired settings

public class MyOptions {
    public string MyDatabase { get; set; }
}

Refactor the setup to also use ConfigureServices and extract the desired configuration to populate the settings object and add it to the service collection

var builder = new HostBuilder();
builder
    .ConfigureWebJobs(b => {
        b.AddAzureStorageCoreServices();
        b.AddAzureStorage();
    })
    .ConfigureAppConfiguration(config => { //not using context so no need for it really
        config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true).Build();
        config.AddEnvironmentVariables();
    })
    //...ADDITION HERE
    .ConfigureServices((context, services) => {
        //Configuration should be available by now, so access what you need.
        var connectionString = context.Configuration.GetConnectionString("MyDatabase");
        //If null you have the option to fail early, otherwise carry on.
        var myOptions = new MyOptions {
            MyDatabase = connectionString,
        };
        services.AddSingleton(myOptions);
    }
    .ConfigureLogging((context, b) => {
        b.AddConsole();
    });

//...

That way at this point you should be able to add your object as a dependency to the function

public class Functions {
    public static void ProcessQueueMessage(
        [QueueTrigger("myqueue")] string message, 
        ILogger logger, 
        MyOptions options) {
        logger.LogInformation(message);
        logger.LogInformation(options.MyDatabase);
    }
}

I personally believe trying to access IConfiguration outside of startup to be more trouble than its worth and would even rank it with service locator anti-pattern and injecting IServiceProvider. Get what you need from it during setup, register that with service collection so it's available for injection where needed explicitly.