1

I have a .NET Core API project and I am trying to read some settings from the appsettings.json file.

The file looks like this:

{
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  },
  "ConnectionString": "abc"
}

and I am trying to read the ConnectionString setting like this:

ConfigurationManager.AppSettings["ConnectionString"];

but I get null, and apparently no app settings have been detected at all.

Edit:

Here is what I have now:

startup.cs

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public static IConfiguration Configuration { get; set; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<MyContext>();
            services.AddMvc();
            services.Configure<ConnectionStringSettings>(Configuration);
        }
}

ConnectionStringSettings.cs

 public class ConnectionStringSettings
    {
        public string ConnectionString { get; set; }
    }

appsettings.json

{
  "Logging": {
    ...
  },
  "ConnectionStrings": {
    "Debug": "abc"
  }
}

MyContext.cs public class MyContext : DbContext { string x;

    public MyContext() { }

    public MyContext(DbContextOptions<MyContext> options)
        : base(options) { }

    public MyContext(IOptions<ConnectionStringSettings> connectionStringSettings)
    {
        x = connectionStringSettings.Value.ConnectionString;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
       //here I need my connection string
        optionsBuilder.UseMySql(connectionString);
    }

}

Eutherpy
  • 4,471
  • 7
  • 40
  • 64
  • 3
    If I am not mistaken, `ConfigurationManager` is for use with the App.config files, and not the new appsettings.json files. You need to use the `ConfigurationBuilder` class as demonstrated in the [following answer](https://stackoverflow.com/questions/31453495/how-to-read-appsettings-values-from-config-json-in-asp-net-core) with `IConfiguration` – ColinM Sep 18 '18 at 12:27

3 Answers3

3

For .NET Core 2.x you need to set configuration up in startup as below

 public partial class Startup
    {
        public static IConfiguration Configuration;

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
    ...
}

Then access as below

Configuration.GetConnectionString("System")

You should also have your connection strings laid out as below.

"ConnectionStrings": {
    "System": "{connection String}"
  }

This will allow for multiple strings if required but work even if you only have the one.

EDIT: Once you've got the string, as ColinM says in his answer you need to register the string with a class which you can inject into your classes. In startup as below.

services.AddSingleton<IConnectionStringFactory, ConnectionStringFactory>(serviceProvider => new ConnectionStringFactory(Configuration.GetConnectionString("System")));

Your connection string class...

public class ConnectionStringFactory : IConnectionStringFactory
{
    private readonly string _connectionString;

    public ConnectionStringFactory(string connectionString)
    {
        _connectionString = connectionString;
    }

    public string Invoke()
    {
        return _connectionString;
    }
}

Inject into your class as so...

public class ClassName
{
    private readonly IConnectionStringFactory _connectionStringFactory;

public ClassName(IConnectionStringFactory connectionStringFactory)
    {
        _connectionStringFactory = connectionStringFactory;
    }

...
}

Your interface can be as simple as below

public interface IConnectionStringFactory
{
}

You don't need to use an interface but I'd recommend this approach

James Morrison
  • 1,954
  • 2
  • 21
  • 48
  • @Eutherpy ColinM's answer goes into that more, you'll want register a DB context with the connection string as per his example. Then any class you need to access it with you can do so via Dependency Injection in the class's constructor. Have updated my answer to show you this with DI – James Morrison Sep 18 '18 at 13:15
  • I've edited my answer with what I've got now, but there are still some missing pieces. Please take a look. – Eutherpy Sep 18 '18 at 13:29
  • @Eutherpy You can (and should) do this configuration in startup, for your example this would look like this - services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("System"))); Replacing your "services.AddDbContext();" line in startup with the above should do the trick. MyContext should also inherit from DbContext - "MyContext : DbContext". If this doesn't work what's the error you're getting? – James Morrison Sep 18 '18 at 13:41
  • @Eutherpy have you gotten this working? If so can you mark the correct answer? – James Morrison Sep 19 '18 at 09:03
  • Thanks for your help, I went with another approach. +1 – Eutherpy Sep 19 '18 at 12:58
2

Based on your requirement of setting the connection string in the OnConfiguring override in your context, you can use the following approach.

Update your appsettings.json configuration file to contain a new JSON object for your database options

{
    "Logging": {
        "LogLevel": {
            "Default": "Warning"
        }
    },
    "DatabaseOptions": {
        "ConnectionString": "<Connection String>"
    }
}

Create your model class which your DatabaseOptions configuration will map to

public class DatabaseOptions
{
    public string ConnectionString { get; set; }
}

Then update your Startup class' ConfigureServices method to register an instance of IOptions<DatabaseOptions>

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.Configure<DatabaseOptions>(Configuration.GetSection("DatabaseOptions"));
}

And finally, update your context constructor to inject your IOptions<DatabaseOptions> instance, ASP.NET Core will handle the dependency injection for you as long as you register your context in the service collection.

public class MyContext
{
    private readonly DatabaseOptions databaseOptions;

    public MyContext(DbContextOptions<MyContext> options, IOptions<DatabaseOptions> databaseOptions)
        : base(options)
    {
        this.databaseOptions = databaseOptions.Value;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        //here I need my connection string
        optionsBuilder.UseMySql(databaseOptions.ConnectionString);
    }
}

Personally, I don't like naming classes with Options in the name when that class is going to be used with IOptions<T>, but as developers we can spend a large time of coding just thinking of class and variable names, feel free to rename as you please.

ColinM
  • 2,622
  • 17
  • 29
  • How should my *appsettings.json* look in this case? – Eutherpy Sep 18 '18 at 12:43
  • I have written this answer around your current *appsettings.json* style. I would definitely recommend using the *ConnectionStrings* JSON property as you can then use the `GetConnectionString` method, and independently encrypt a section of that config file. – ColinM Sep 18 '18 at 12:45
  • I have updated my answer to work with the *ConnectionStrings* property – ColinM Sep 18 '18 at 12:50
  • I've edited my answer with what I've got now, but there are still some missing pieces. Please take a look. – Eutherpy Sep 18 '18 at 13:29
  • The problem is that you're mixing both of my answers. If you're using *ConnectionStrings* then you shouldn't use `services.Configure`, instead just use `var connectionString = Configuration.GetConnectionString("Default"); services.AddDbContext(options => options.UseSqlServer(connectionString);` – ColinM Sep 18 '18 at 13:40
  • My problem is that my `optionsBuilder.UseMySql(connectionString);` has to stay in the `OnConfiguring()` method, for some other reasons. – Eutherpy Sep 18 '18 at 13:47
  • @Eutherpy, rewrote answer based on your requirements. – ColinM Sep 18 '18 at 20:03
  • I did all this, but for some reason the parameterless constructor for `MyContext` (which I need) is called instead of the one with parameters, so my `databaseOptions` in `OnConfiguring()` is `null`. – Eutherpy Sep 19 '18 at 08:45
  • Why do you need a parameterless constructor? – ColinM Sep 19 '18 at 08:46
  • That's how I instantiate my context in the controllers. – Eutherpy Sep 19 '18 at 08:48
  • What's wrong with `services.AddDbContext()`, and injecting your context? – ColinM Sep 19 '18 at 08:49
  • For some reason that didn't work for me. See my question https://stackoverflow.com/questions/52295259/asp-net-api-no-database-provider-has-been-configured-for-this-dbcontext – Eutherpy Sep 19 '18 at 08:55
0

You should use Configuration in Startup =>

    public void ConfigureServices(IServiceCollection services)
    {
       var section = Configuration.GetSection("ConnectionString");
       var value= Configuration.GetValue<string>("ConnectionString")
    }
Hamit Gündogdu
  • 204
  • 1
  • 13