从 appsettings.json 获取 ConnectionString 而不是在 .NET Core 2.0 App 中进行硬编码

Get ConnectionString from appsettings.json instead of being hardcoded in .NET Core 2.0 App

我在.NET Core2.0 App中有以下class

// 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);
    }
}

这在 Core 2.0 中是必需的,当数据库不存在时需要进行迁移,并且必须在 运行 update-database.
时创建

我不想在 2 个地方(此处和 appsettings.json 中)使用 ConnectionString,而只在 .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 被迁移使用所以应用程序是一点也不 运行ning。

解决方案: 根据@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);
}

如何将它作为 dp 注入传递到 class? 在配置服务中:

services.Configure<MyOptions>(Configuration);

创建 class 来容纳 json 个字符串:

public class MyOptions
{
    public MyOptions()
    {

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

将字符串添加到 json 文件:

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

在需要这些字符串的 classes 中,作为构造函数传入:

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
 }
}

第 1 步:在 OnConfiguring() 中包含以下内容

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

第 2 步:创建 appsettings.json:

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

第 3 步:硬拷贝 appsettings.json 到正确的目录

  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.        

假设:您已经在项目中包含包 Microsoft.Extensions.Configuration.Json(从 Nuget 获取)。

实际上有一个默认模式,您可以使用它来实现此结果,而无需实施 IDesignTimeDbContextFactory 和复制任何配置文件。

this doc 中有详细说明,其中还讨论了框架在设计时尝试实例化您的 DbContext 的其他方式。

具体来说,您利用了一个新的挂钩,在本例中是 public static IWebHost BuildWebHost(string[] args) 形式的静态方法。文档另有暗示,但此方法可以存在于 class 中的任何一个入口点(see src). Implementing this is part of the guidance in the 1.x to 2.x migration document 并且从代码中看并不完全明显的是对 WebHost.CreateDefaultBuilder(args) 的调用是,除其他外事情,以新项目开始时使用的默认模式连接您的配置。这就是让设计时服务(如迁移)使用的配置所需的全部内容。

这里有更多关于里面发生的事情的详细信息:

添加迁移时,当框架尝试创建您的 DbContext 时,它 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 looks 通过任何 DbContext 派生的 classes 的程序集并创建一个工厂只调用 Activator.CreateInstance 作为最后的万岁玛丽的方法。

框架使用的优先顺序同上。因此,如果您实现了 IDesignTimeDbContextFactory,它将覆盖上面提到的钩子。不过,对于大多数常见情况,您不需要 IDesignTimeDbContextFactory.

在 ASPNET Core 中,您可以在 Startup.cs

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

您的连接在 appsettings.json

中定义
{
  "ConnectionStrings": {
    "BloggingDatabase": "..."
  },
}

示例来自 MS docs

您也可以在 ASP.NET Core 2 中通过在 appSettings.json 文件中定义连接字符串来执行此操作。然后在 Startup.cs 中指定要使用的连接字符串。

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. 将以下代码添加到 startup.cs 文件中。

    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. Contextclass中使用GlobalProperties.DBConnection属性。

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

我知道这已被标记为已回答,但我 运行 在处理一个我有 EF 核心数据访问层的项目时遇到了一些问题 在与我的项目的其余部分分开的 .DLL 项目中,API、Auth 和 Web,并且大多数情况下我的其他项目都会引用此数据项目。而且我不想每次都进入数据项目来更改连接字符串。

第 1 步:将其包含在 OnConfiguring 方法中

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"));
      }

注意:.SetBasePath(Path.Combine(Directory.GetCurrentDirectory())) 这将否定或使将文件复制到目录的需要无效或无效,因为 ASP.NET CORE 足够聪明,可以选择正确的文件。此外,假设选择了 Prod 环境文件,指定的环境将在构建发布或生产环境时选择正确的文件。

第 2 步:创建 appsettings.json

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

}

请:参考:Microsoft.Extensions.Configuration

我知道这并不奇怪,但您可以使用回调 class,创建主机构建器并将配置设置为静态 属性。

对于 asp 核心 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
    }
}

所以现在您只需在您需要的任何其他 class 处使用静态 Program.Configuration

上面的解决方案和 Microsoft 文档中都缺少一些东西。如果您按照 link 从上述文档 link 找到 GitHub 存储库,您将找到真正的解决方案。

我认为混淆在于许多人使用的默认模板包含 Startup 的默认构造函数,因此人们不一定知道在哪里注入的配置来自。

因此,在 Startup.cs 中添加:

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

然后在 ConfigureServices 方法中添加其他人所说的...

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

您还必须确保已创建 appsettings.json 文件并具有与此类似的连接字符串部分

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

当然,您必须对其进行编辑以反映您的配置。

注意事项。这是在 .Net Standard 2.1 项目中使用 Entity Framework Core 3 测试的。 我需要为以下项目添加 nuget 包: Microsoft.EntityFrameworkCore 3.0.0 Microsoft.EntityFrameworkCore.SqlServer 3.0.0,因为这是我正在使用的,也是访问 UseSqlServer 所需的。

如果你需要在不同的图层:

创建静态 Class 并公开该层上的所有配置属性,如下所示:

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;
        }
    }
}