'Appsettings values not being read running EF migration in Devops build pipeline

When running a command line task in a Devops build pipeline to create an EF migration SQL script, the values from AppSettings.json (and the IConfigurationSection derived from them) are not populated. They are always null.

This is an issue as there are several configuration classes which are instantiated and used within the Startup.ConfigureServices() method of this .Net Core 3.1 project.

The eg migration script command line works fine when running locally, but not when executed as part of the build pipeline. Are there any configuration steps within devops I can take to ensure that the appsettings.json file is read and successfully loaded within the ConfigureServices() call? Is there a step which is being missed?

For reference this is the Devops task:

- task: CmdLine@2
  displayName: "Build EF Migrations"
  inputs:
    script: |
      dotnet tool install --global dotnet-ef
      dotnet ef migrations script -p ProjectName.Domain -s ProjectName.API -o $(Build.ArtifactStagingDirectory)\migrations\scripts.sql -i -v

This is the simplified version of the ConfigureServices() logic:

public void ConfigureServices(IServiceCollection services)
{
  // Issue: all properties in the below classes are always null
  var jwtConfiguration = Configuration.GetSection("Jwt").Get<JwtConfiguration>();
  var storageConfiguration = Configuration.GetSection("Storage").Get<StorageConfiguration>();

  services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(storageConfiguration.Sql.ConnectionString));
  services.ConfigureJwt(jwtConfiguration);

  // other configuration settings...
}

Finally, these are the errors as taken from the verbose output of the EF command in the Devops task:

System.ArgumentNullException: String reference not set to an instance of a String. (Parameter 's')  
 at System.Text.Encoding.GetBytes(String s)  
 at Project.API.StartupExtensions.ConfigureJwt(IServiceCollection services, JwtConfiguration jwtConfiguration) in D:\a\1\s\PatientGo.API\Startup.Jwt.cs:line 22  
 at Project.API.Startup.ConfigureServices(IServiceCollection services) in D:\a\1\s\PatientGo.API\Startup.cs:line 46  

System.MissingMethodException: No parameterless constructor defined for type 'PatientGo.Domain.Data.ApplicationDbContext'.

Line 22 of Startup.Jwt.cs is the first time in the code where a member of the JwtConfiguration class is accessed.

The latter error is because storageConfiguration.Sql.ConnectionString is null when it should be filled with the connection string from appsettings.json



Solution 1:[1]

To solve your problem you have two options:

Create no parameter constructor in your ApplicationDbContext and use OnConfiguring method to pass options:

public class ApplicationDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test");
    }
}

Implement IDesignTimeDbContextFactory interface and tell how to instantiate your context. What is funny that I see in my project even connection string is wrong is still is able to generate scripts in Azure DevOps. So it will try to connect to database only when you run command to generate migration. Also i think if you do this way you wont need to pass -s ProjectName.API

public class ApplicationContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
            optionsBuilder.UseSqlServer("server=(local);database=your_db;Integrated Security=SSPI;MultipleActiveResultSets=True;");

            return new ApplicationDbContext (optionsBuilder.Options);
    }
}

In my case this works in Azure devOps and it does not complain

PS: As Panagiotis Kanavos pointed dont hardcode those values my answer is just pointing you to how you can fix :)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1