5

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?

9
  • Is this WebJobs or Azure Functions? Commented Jun 18, 2019 at 20:32
  • 1
    It is a WebJob. Commented Jun 18, 2019 at 20:47
  • 1
    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>()? Commented Jun 18, 2019 at 20:51
  • 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. Commented Jun 18, 2019 at 21:06
  • 1
    Well make it not static! Commented Jun 18, 2019 at 21:11

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

11 Comments

Thanks Nkosi, I'm going to give this a shot. I am wondering, though, is this uncommon? Surely I can't be the only one trying to get my configuration properties from within a helper function in a .Net Core WebJob. Yet I can't seem to find any examples of it anywhere.
For me, the lack of examples showing that are telling on their own.
Which is a concern for me. Makes me wonder if I am not approaching this right.
Hi @Nkosi, I like this approach. However, when I attempt it, I am getting the same error as before (applied to MyOptions): InvalidOperationException: Cannot bind parameter 'options' to type MyOptions. 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.). Any thoughts on that? I would like to make it work this way if possible.
static was the original way then they moved to the instance model with DI
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.