简体   繁体   中英

How to set connectionstring from appsettings.json in Entity Framework Core

I'm setting up a new database with Entityframework Core and code first approach. I have set up a context class like in the code listing below and all needed classes for the database tables too. Now I want to create the new database with

using (var context = new MyContext())
{
    context.Database.EnsureCreated();
}

But what I get is an error, that the connectionstring cannot be null.

My Connectionstring is set in the appsettings.json file an I copy this to the output directory when the project builds.

I have tried different ways to get the connectionstring from the appsettings file but all with the same result. In the Configuration Property in the Startup class I can see that the appsettings file is loaded correctly but when I want to get the string with

ConnectionString = Configuration["Connectionstrings:MyConnection"];

the ConnectionString is allways null.

I have this in my Startup.cs File:

 public class Startup
 {
    public static string ConnectionString { get; private set; }
    public IConfigurationRoot Configuration { get; set; }

    public Startup(IHostingEnvironment _environment)
    {
        Configuration = new ConfigurationBuilder()
                        .SetBasePath(_environment.ContentRootPath)
                        .AddJsonFile("appsettings.json")
                        .Build();
    }

with the following in the Configure - Method

using (var context = new MyContext())
{
    context.Database.EnsureCreated();
}

And in my context class I have the following code

public class MyContext : DbContext
{
    public MyContext()
    {
    }

    public static string GetConnectionString()
    {
        return Startup.ConnectionString;
    }

protected override void OnConfiguring(DbContextOptionsBuilder _builder)
{
    _builder.UseSqlServer(GetConnectionString());
}

So now, when I start the application I get the error in the OnConfiguring - Method that the connectionstring cannot be null.

And my appsettings.json file is

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },

  "Connnectionstrings": {
    "MyConnection": "server=.;database=MyDatabase;trusted_connection=true;MultipleActiveResultSets=True"
  },
}

The ConnectionString is allways null because there is a typo, try this

ConnectionString = Configuration["Connnectionstrings:MyConnection"];

or fix name in appsettings.json

I can show you two options of how to pass connection string via strongly typed Settings class loaded from appsettings.json . For both you just need to move your config settings to custom section from default ConnnectionStrings one and fully control your DB settings and passing them to you EntityFramework context.

You need do this in Startup class:

private Settings _settings;
public Startup(IConfiguration configuration, ...)
{
    Configuration = configuration;
    ...
}

public IConfiguration Configuration { get; }
...
public void ConfigureServices(IServiceCollection services)
{
    services
        .AddOptions()
        .Configure<Settings>(Configuration.GetSection("Settings"))
        .AddSingleton(Configuration);

    _settings = Configuration.GetSection(nameof(Settings)).Get<Settings>();

    services.AddTransient<DesignTimeDbContextFactory>();

    services.AddDbContext<MyContext>(options =>
    {
        if (_settings.DatabaseSettings.UseInMemory)
        {
            options.UseInMemoryDatabase("DummyInMemoryDatabase");
        }
        else
        {
            // Option 1
            options.UseSqlServer(_settings.DatabaseSettings.BuildConnectionString());
            // Option 2
            options.UseSqlServer(_settings.ConnectionStrings.MyConnection);
        }
    });
    ...
}

This is the corresponding appsettings.json file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  },

  "Settings": {

    // Option 1: move connection strings to custom strong typed Settings class
    "ConnnectionStrings": {
      "MyConnection": "server=.;database=MyDatabase;trusted_connection=true;MultipleActiveResultSets=True"
    },

    // Option 2: store DB settings any custom way you like - also to get via strong typed Settings class
    "DatabaseSettings": {
      "UseInMemory": false,
      "Host": ".",
      "Name": "MyDatabase",
      "User": "qwerty",
      "Password": "@#$%*"
    }
  }
}

The strongly typed Settings class:

/// <summary> Represents settings configured in appsettings.json file </summary>
public class Settings
{
    /// <summary> Option 1: move connection strings to custom strong typed Settings class </summary>
    public Connections ConnectionStrings { get; set; }

    /// <summary> Option 2: store DB settings any custom way you like - also to get via strong typed Settings class </summary>
    public Database DatabaseSettings { get; set; }
}

public class Connections
{
    public string MyConnection { get; set; }
}

public class Database
{
    public bool UseInMemory { get; set; }
    public string Host { get; set; }
    public string Name { get; set; }
    public string User { get; set; }
    public string Password { get; set; }
    public string BuildConnectionString() => $"Server={Host};Database={Name};User Id={User};Password={Password}";
}

And helper class which is used for DB migrations and other design time code generations - DesignTimeDbContextFactory :

public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyContext>
{
    public  MyContext CreateDbContext(string[] args)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
            .AddJsonFile("appsettings.json", optional: false);

        var config = builder.Build();

        var settingsSection = config.GetSection("Settings");
        var settings = new Settings();
        settingsSection.Bind(settings);

        var optionsBuilder = new DbContextOptionsBuilder<MyContext>()
            .UseSqlServer(settings.ConnectionStrings.MyConnection); // or you can use option #2 either

        return new MyContext(optionsBuilder.Options);
    }
}

The following template adopts patterns that I stole from asp.net core.

appsettings.json

{
  "ConnectionStrings": {
    "MyConnection": "Data Source=WithSqlite",
    "YourConnection": "Server=(localdb)\\mssqllocaldb;Database=WithSqlServer;Trusted_Connection=True"
  }
}

AppDbContext.cs

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
    // DbSet goes here
}

Program.cs

static class Program
{
    private static readonly IConfiguration _configuration;

    static Program()
    {
        _configuration = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .Build();
    }

    private static void ConfigureServices(IServiceCollection isc)
    {
        isc.AddSingleton(_configuration);

        isc.AddDbContext<AppDbContext>(options =>
        {

            options.UseSqlite(_configuration["ConnectionStrings:MyConnection"]);

            //options .UseSqlServer(_configuration.GetConnectionString("YourConnection"));


        });

        isc.AddSingleton<TheApp>();


    }

    public static  ServiceProvider CreateServiceProvider()
    {
        // create service collection
        IServiceCollection isc = new ServiceCollection();
        ConfigureServices(isc);

        // create service provider
        return isc.BuildServiceProvider();
    }

    private static void Main(string[] args)
    {
        // create application instance and run
        using (var scope = CreateServiceProvider().CreateScope())
        {
            scope.ServiceProvider.GetRequiredService<TheApp>().Run();
            // It is said that GetRequiredService is much better than GetService.
        }
    }
}

TheApp.cs

class TheApp
{
    private readonly AppDbContext _db;


    public TheApp(AppDbContext db)
    {
        _db = db;
    }

    public void Run()
    {

    // Your main logic goes here

    }

}

MigrationHelper.cs

class MigrationHelper: IDesignTimeDbContextFactory<AppDbContext>
{
    public AppDbContext CreateDbContext(string[] args) => 
        Program.CreateServiceProvider()
        .CreateScope()
        .ServiceProvider
        .GetService<AppDbContext>();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM