Azure Functions environment variable is always null

Viewed 2490

In my V3 functions project I am trying to wire up Entity Framework and pass a connection string from an environment variable.

In my local.settings.json file I have:

{
  "Values" : {
    "SqlConnectionString" : "<localdb connection string here>"
  }
}

I am wiring up the context using IDesignTimeContextFactory as per this blog post:

public class ContextFactory : IDesignTimeDbContextFactory<Context>
    {
        public Context CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<Context>();
            var connectionString = Environment.GetEnvironmentVariable("SqlConnectionString", EnvironmentVariableTarget.Process);
            optionsBuilder.UseSqlServer(connectionString);

            return new Context(optionsBuilder.Options);
        }
    }

When I try adding a migration connectionString is null.

Can anyone see what the problem is here?

Edit:

As suggested, I've also tried defining the connection string and injecting IConfiguration like this:

    public class ContextFactory : IDesignTimeDbContextFactory<Context>
    {
        private readonly IConfiguration _configuration;

        public ContextFactory(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public Context CreateDbContext(string[] args)
        {
            var optionsBuilder = new DbContextOptionsBuilder<Context>();
            optionsBuilder.UseSqlServer(_configuration.GetConnectionString("SqlConnectionString"));

            return new Context(optionsBuilder.Options);
        }
    }

but I get the following exception:

System.MissingMethodException: No parameterless constructor defined for type 'EbayTools.Functions.Domain.ContextFactory'.

4 Answers

EXPLANATION

I ran into the same problem while following the same blog post/tutorial!

When running the Entity Framework Core CLI tools the values in local.settings.json are not added to the list of environment variables. You can check this by prompting the debugger to run (see code).

To access a value in the local.settings.json when running an Entity Framework Core CLI command I used the ConfigurationBuilder (again see below).

Lastly, I stored my connection string under 'Values' in the local.settings.json file, not ConnectionStrings.


SOLUTION

ProjectContextFactory.cs

public class ProjectContextFactory : IDesignTimeDbContextFactory<ProjectDbContext>
{
    public ProjectDbContext CreateDbContext(string[] args)
    {
        // Prompt debugger to open when running ef cli command.
        // Remove this when you no longer need to debug
        Debugger.Launch();

        // See that environment variables list doesn't contain your key value and therefore connectionString variable returns null
        var variables = Environment.GetEnvironmentVariables();
        var connectionString = Environment.GetEnvironmentVariable("SQLDB_CONNECTIONSTRING");

        // Setup config
        var config = new ConfigurationBuilder()
            .SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();

        // Get connection string from config
        var connectionString = config.GetValue<string>("Values:SQLDB_CONNECTIONSTRING");

        var optionsBuilder = new DbContextOptionsBuilder<ProjectDbContext>();
        optionsBuilder.UseSqlServer(connectionString);

        return new ProjectDbContext(optionsBuilder.Options);
    }
}

local.settings.json

{
   "IsEncrypted": false,
   "Values": { 
       "AzureWebJobsStorage": "UseDevelopmentStorage=true",
       "SQLDB_CONNECTIONSTRING": "<connectionString>
   }
}

You can inject IConfiguration in Startup() like below:

var configuration = builder.Services.BuildServiceProvider().GetService<IConfiguration>(); 
var connString = configuration.GetConnectionString("DataContext");

With local.settings.json file:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
  },
  "ConnectionStrings": {
    "StorageConnectionString": "aaaa",
  }
}

IConfiguration will automatically read from your function app settings when hosting in Azure, and your local.settings.json when run locally.

For your error message you can refer to this article.

In case visual studio 2019 does not work for me.

This solution applies to azure function created using Visual studio code only

Access environment variable like this-

var APIToInvoke = System.Environment.GetEnvironmentVariable("APIToInvoke", EnvironmentVariableTarget.Process);

But imp, the azure function should be created & deployed with visual studio code. After deployment, you will see button-upload settings, use it to upload settings from the local.settings.json file. Then it will work.

enter image description here

You are doing wrong Environment.GetEnvironmentVariableretrieves the value of an environment variable.

I would suggest you to define the connection string in appsettings.json file and use it like this.

appsettings.json

  "ConnectionStrings": {
    "DB": "Data Source=Server;Initial Catalog=DBName;User ID=UserId;Password=password-"
  }

C# Code

private IConfiguration _configuration { get; set; }
_configuration.GetConnectionString("DB");

You need to inject the IConfiguration into the required class.

Related