Sixthpoint
Sixthpoint

Reputation: 1193

EF core unable to resolve environment variable using UseSqlServer with SpecFlow

I am converting a BDD test project to the use EF core 5.x and SpecFlow 3.x + Specflow.Autofac. When executing the scenario the context is unable to find the connection string from the environmental variables.

Solution setup has the following structure

The scenario dependencies are registered before all scenarios are executed;

public static class DependencyResolver
{
    private static IConfiguration config;

    [ScenarioDependencies]
    public static ContainerBuilder CreateContainerBuilder()
    {
        var containerBuilder = new ContainerBuilder();
        if (config == null)
        {
            string fullPath = System.Reflection.Assembly.GetAssembly(typeof(DependencyResolver)).Location;
            string theDirectory = Path.GetDirectoryName(fullPath);

            config = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .SetBasePath(theDirectory)
                .AddEnvironmentVariables()
                .Build();
        }

        containerBuilder.RegisterInstance(config);
        containerBuilder.RegisterTypes(typeof(DependencyResolver).Assembly.GetTypes().Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))).ToArray()).SingleInstance(); 
        containerBuilder.RegisterModule(new DependencyRegister());
        containerBuilder.RegisterModule(new BusinessRegister());
        containerBuilder.RegisterModule(new DataRegister());
        return containerBuilder;
    }
}

The DataRegister registers MyContext;

public class DataRegister : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<MyContext>().AsSelf().InstancePerDependency();
    }
}

Inside MyContext it calls UseSqlServer to attempt to resolve a environment variable

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.EnableDetailedErrors();

    if (!optionsBuilder.IsConfigured)
    {
           optionsBuilder.UseSqlServer("Name=ConnectionStrings:MyContext");
    }
}

The appsettings.json located in the BddTest project

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.EntityFrameworkCore": "Debug",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "MyContext": "--"
  }
}

When I attempt to run a scenario the application errors out when attempting to create a database connection inside of MyContext;

System.InvalidOperationException: 'A named connection string was used, but the name 'ConnectionStrings:MyContext' was not found in the application's configuration. Note that named connection strings are only supported when using 'IConfiguration' and a service provider, such as in a typical ASP.NET Core application.

How do I register my appsettings.json so that it is available inside the Data project and can be resolved when using optionsBuilder.UseSqlServer("Name=ConnectionStrings:MyContext");?

Upvotes: 2

Views: 897

Answers (3)

Sixthpoint
Sixthpoint

Reputation: 1193

To solve the issue I did something very similar to what @Matheus Dasuke was suggesting. Injected the IConfiguration then read out the values. This means I can get the values of the properties from environmental vars, user secrets, etc.

private static IConfiguration _configuration;

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

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
    optionsBuilder.EnableDetailedErrors();

    if (!optionsBuilder.IsConfigured) {
        var builder = new NpgsqlConnectionStringBuilder("Host=" +
            _configuration.GetConnectionString("Host")) {
            Password = _configuration["Password"],
                Username = _configuration["Username"],
                Database = _configuration["Database"],
        };
        optionsBuilder.UseNpgsql(builder.ConnectionString);
    }
}

Upvotes: 1

Matheus Dasuke
Matheus Dasuke

Reputation: 261

Try this:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.EnableDetailedErrors();

    if (!optionsBuilder.IsConfigured)
    {
       var config = ConfigurationManager.ConnectionStrings["MyContext"];
       var conn = new SqlConnection(config.ConnectionString);

       options.UseSqlServer(conn);
    }
}

Upvotes: 1

Juanma Feliu
Juanma Feliu

Reputation: 1346

Use:

_config.GetValue<string>("ConnectionStrings:MyContext")

Where _config is a DI IConfiguration injected in your constructor where OnConfiguring is.

Upvotes: 0

Related Questions