从 appsetings.json 获取 ConnectionString,而不是在.NET Core 2.0 App 中硬编码

我在 NET Core2.0应用程序中有以下类。

// required when local database does not exist or was deleted
public class ToDoContextFactory : IDesignTimeDbContextFactory<AppContext>
{
public AppContext CreateDbContext(string[] args)
{
var builder = new DbContextOptionsBuilder<AppContext>();
builder.UseSqlServer("Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true");
return new AppContext(builder.Options);
}
}

当数据库不存在并且必须在运行 更新资料库时创建时,这在 Core2.0迁移中是必需的。
升级到 ASP.NET Core 2.0后无法创建迁移

我不希望 ConnectionString 出现在两个地方(这里和 appsetings.json 中) ,而只出现在. json 中 所以我试图替换

"Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true"

ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString

但是没有用,我得到的是空值。

更新1:
只要注意明确地添加。Json 在 Core 2中不是必需的,所以问题不在于文件。
Https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/

更新2:
此外,我已经在使用 Configuration 将 ConnectionString 从.json 发送到 Context:

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


public IConfiguration Configuration { get; }


public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<AppContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}
}

但是我不能使用这个对于 ToDoContextFactory,因为它没有配置,而且 ToDoContextFactory是由迁移使用的,所以应用程序根本不运行。

解决方案: 根据@JRB 的回答,我把它设计成这样:

public AppContext CreateDbContext(string[] args)
{
string projectPath = AppDomain.CurrentDomain.BaseDirectory.Split(new String[] { @"bin\" }, StringSplitOptions.None)[0];
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(projectPath)
.AddJsonFile("appsettings.json")
.Build();
string connectionString = configuration.GetConnectionString("DefaultConnection");


var builder = new DbContextOptionsBuilder<AppContext>();
builder.UseSqlServer(connectionString);


return new AppContext(builder.Options);
}
316305 次浏览

How about passing it as dp injection into that class? in ConfigureServices:

services.Configure<MyOptions>(Configuration);

create class to hold json strings:

public class MyOptions
{
public MyOptions()
{


}
public string Option1 { get; set; }
public string Option2 { get; set; }
}

Add strings to json file:

"option1": "somestring",
"option2": "someothersecretstring"

In classes that need these strings, pass in as constructor:

public class SomeClass
{
private readonly MyOptions _options;


public SomeClass(IOptions<MyOptions> options)
{
_options = options.Value;
}


public void UseStrings()
{
var option1 = _options.Option1;
var option2 = _options.Option2;
//code
}
}

STEP 1: Include the following in OnConfiguring()

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
}

STEP 2: Create appsettings.json:

  {
"ConnectionStrings": {
"DefaultConnection": "Server=YOURSERVERNAME; Database=YOURDATABASENAME; Trusted_Connection=True; MultipleActiveResultSets=true"
}
}

STEP 3: Hard copy appsettings.json to the correct directory

  Hard copy appsettings.json.config to the directory specified in the AppDomain.CurrentDomain.BaseDirectory directory.
Use your debugger to find out which directory that is.

Assumption: you have already included package Microsoft.Extensions.Configuration.Json (get it from Nuget) in your project.

There is actually a default pattern that you can employ to achieve this result without having to implement IDesignTimeDbContextFactory and do any config file copying.

It is detailed in this doc, which also discusses the other ways in which the framework will attempt to instantiate your DbContext at design time.

Specifically, you leverage a new hook, in this case a static method of the form public static IWebHost BuildWebHost(string[] args). The documentation implies otherwise, but this method can live in whichever class houses your entry point (see src). Implementing this is part of the guidance in the 1.x to 2.x migration document and what's not completely obvious looking at the code is that the call to WebHost.CreateDefaultBuilder(args) is, among other things, connecting your configuration in the default pattern that new projects start with. That's all you need to get the configuration to be used by the design time services like migrations.

Here's more detail on what's going on deep down in there:

While adding a migration, when the framework attempts to create your DbContext, it first adds any IDesignTimeDbContextFactory implementations it finds to a collection of factory methods that can be used to create your context, then it gets your configured services via the static hook discussed earlier and looks for any context types registered with a DbContextOptions (which happens in your Startup.ConfigureServices when you use AddDbContext or AddDbContextPool) and adds those factories. Finally, it IDesignTimeDbContextFactory0 through the assembly for any DbContext derived classes and creates a factory method that just calls Activator.CreateInstance as a final hail mary.

The order of precedence that the framework uses is the same as above. Thus, if you have IDesignTimeDbContextFactory implemented, it will override the hook mentioned above. For most common scenarios though, you won't need IDesignTimeDbContextFactory.

In ASPNET Core you do it in Startup.cs

public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BloggingContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));
}

where your connection is defined in appsettings.json

{
"ConnectionStrings": {
"BloggingDatabase": "..."
},
}

Example from MS docs

You can also do this in ASP.NET Core 2 by defining the connection string in your appSettings.json file. Then in your Startup.cs you specify which connection string to use.

appSettings.json

{
"connectionStrings": {
"YourDBConnectionString": "Server=(localdb)\\mssqllocaldb;Database=YourDB;Trusted_Connection=True"
}
}

Startup.cs

public static IConfiguration Configuration { get; private set;}


public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
var connectionString = Configuration["connectionStrings:YourDBConnectionString"];
services.AddDbContext<YourDbContext>(x => x.UseSqlServer(connectionString));
  1. Add the following code into startup.cs file.

    public void ConfigureServices(IServiceCollection services)
    {
    string con = Configuration.GetConnectionString("DBConnection");
    services.AddMvc();
    GlobalProperties.DBConnection = con;//DBConnection is a user defined static property of GlobalProperties class
    }
    
  2. Use GlobalProperties.DBConnection property in Context class.

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
    if (!optionsBuilder.IsConfigured)
    {
    optionsBuilder.UseSqlServer(GlobalProperties.DBConnection);
    }
    }
    

I understand this has been marked as answered but I ran into a bit of a problem when I was working on a project where I have my EF Core Data Access Layer in a .DLL Project separated from the rest of my project, API, Auth and Web and mostly will like my other projects to reference this Data project. And I don't want to want to come into the Data project to change connection strings everytime.

STEP 1: Include this in the OnConfiguring Method

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var envName = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
IConfigurationRoot configuration = new ConfigurationBuilder()
**.SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))**
.AddJsonFile("appsettings.json", optional: false)
.AddJsonFile($"appsettings.{envName}.json", optional: false)
.Build();
optionsBuilder.UseSqlServer(configuration.GetConnectionString("DefaultConnection"));
}

NOTE: .SetBasePath(Path.Combine(Directory.GetCurrentDirectory())) This will negate or invalidate the need to copy the file to a directory as ASP.NET CORE is smart enough to pick the the right file. Also the environment specified will pick right file when the building for Release or Production, assuming the Prod environment file is selected.

STEP 2: Create appsettings.json

{
"ConnectionStrings": {
"DefaultConnection": "Server=YOURSERVERNAME; Database=YOURDATABASENAME; Trusted_Connection=True; MultipleActiveResultSets=true"
}

}

PLEASE: Referece: Microsoft.Extensions.Configuration

It's not fancy I known but you could use a callback class, create a hostbuilder and set the configuration to a static property.

For asp core 2.2:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using System;


namespace Project
{
sealed class Program
{
#region Variables
/// <summary>
/// Last loaded configuration
/// </summary>
private static IConfiguration _Configuration;
#endregion


#region Properties
/// <summary>
/// Default application configuration
/// </summary>
internal static IConfiguration Configuration
{
get
{
// None configuration yet?
if (Program._Configuration == null)
{
// Create the builder using a callback class
IWebHostBuilder builder = WebHost.CreateDefaultBuilder().UseStartup<CallBackConfiguration>();


// Build everything but do not initialize it
builder.Build();
}


// Current configuration
return Program._Configuration;
}


// Update configuration
set => Program._Configuration = value;
}
#endregion


#region Public
/// <summary>
/// Start the webapp
/// </summary>
public static void Main(string[] args)
{
// Create the builder using the default Startup class
IWebHostBuilder builder = WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();


// Build everything and run it
using (IWebHost host = builder.Build())
host.Run();
}
#endregion




#region CallBackConfiguration
/// <summary>
/// Aux class to callback configuration
/// </summary>
private class CallBackConfiguration
{
/// <summary>
/// Callback with configuration
/// </summary>
public CallBackConfiguration(IConfiguration configuration)
{
// Update the last configuration
Program.Configuration = configuration;
}


/// <summary>
/// Do nothing, just for compatibility
/// </summary>
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//
}
}
#endregion
}
}

So now on you just use the static Program.Configuration at any other class you need it.

There are a couple things missing, both from the solutions above and also from the Microsoft documentation. If you follow the link to the GitHub repository linked from the documentation above, you'll find the real solution.

I think the confusion lies with the fact that the default templates that many people are using do not contain the default constructor for Startup, so people don't necessarily know where the injected Configuration is coming from.

So, in Startup.cs, add:

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

and then in ConfigureServices method add what other people have said...

services.AddDbContext<ChromeContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")));

you also have to ensure that you've got your appsettings.json file created and have a connection strings section similar to this

{
"ConnectionStrings": {
"DatabaseConnection": "Server=MyServer;Database=MyDatabase;Persist Security Info=True;User ID=SA;Password=PASSWORD;MultipleActiveResultSets=True;"
}
}

Of course, you will have to edit that to reflect your configuration.

Things to keep in mind. This was tested using Entity Framework Core 3 in a .Net Standard 2.1 project. I needed to add the nuget packages for: Microsoft.EntityFrameworkCore 3.0.0 Microsoft.EntityFrameworkCore.SqlServer 3.0.0 because that's what I'm using, and that's what is required to get access to the UseSqlServer.

If you need in different Layer :

Create a Static Class and expose all config properties on that layer as below :

using Microsoft.Extensions.Configuration;
using System.IO;


namespace Core.DAL
{
public static class ConfigSettings
{
public static string conStr1 { get ; }
static ConfigSettings()
{
var configurationBuilder = new ConfigurationBuilder();
string path = Path.Combine(Directory.GetCurrentDirectory(), "appsettings.json");
configurationBuilder.AddJsonFile(path, false);
conStr1 = configurationBuilder.Build().GetSection("ConnectionStrings:ConStr1").Value;
}
}
}

just use

string connectionString =
builder.Configuration.GetConnectionString("TestDBConnectionString");

If you are using "top level statements", you can get your connection string as follows:

var builder = WebApplication.CreateBuilder(args);


var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
        var builder = WebApplication.CreateBuilder();
string conStr = builder.Configuration.GetConnectionString("myDb1");
SqlConnection conn = new SqlConnection(conStr);