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

Posted

技术标签:

【中文标题】从 appsettings.json 获取 ConnectionString,而不是在 .NET Core 2.0 App 中硬编码【英文标题】:Get ConnectionString from appsettings.json instead of being hardcoded in .NET Core 2.0 App 【发布时间】:2018-01-29 12:12:45 【问题描述】:

我在 NET Core2.0 App 中有以下类。

// 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 时创建。Unable to create migrations after upgrading to ASP.NET Core 2.0

我不想在 2 个地方(这里和 appsettings.json)有 ConnectionString,但只在 .json 所以我试图替换

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

ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString

但它不起作用,我得到的是空值。

更新 1: 请注意,在 Core 2 中不需要显式添加 .json,因此问题不在于文件。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);

【问题讨论】:

不知道最新版本,但在早期版本中,您仍然需要将.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) 添加到您的ConfigurationBuilder。你做过吗? “正常”应用程序设置是否正常工作? 在 Core2.0 中这是自动完成的:andrewlock.net/… 在 Core2.0 中,您可以使用 System.AppContext.BaseDirectory 获取基本路径,以防您在启动时无法做到这一点,如提及 @borisdj:github.com/aspnet/Announcements/issues/237 对于那些想知道SetBasePath 来自哪里的人:***.com/questions/46843367/… 和AddJsonFile 来自哪里:***.com/questions/27382481/… 谢谢!我认为您应该在解决方案中发布单独的答案,而不是将其嵌入到您的问题中。 【参考方案1】:

第 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 获取)。

【讨论】:

我不同意! “复制appsettings.json”,里面包含连接字符串,就像在两个地方声明连接字符串一样糟糕!谁将负责保持同步? 我和 borisdj 遇到的问题的背景是 .NET Core 2.0 System.Configuration 不再存在,需要更换。为了让其他人更容易理解答案,我在步骤 3 中选择了硬拷贝方法。但是您当然是正确的,在现实世界的场景中,您会将“appsetting.json”添加到您的项目中,并将“属性页面”中“常规-高级”选项卡中的“复制到输出目录”设置为“始终复制” ",之后不再需要手动复制操作。 如果您想从appsettings.json 的自定义部分获取连接字符串,请查看***.com/a/53924899/804385 在哪里可以找到 OnConfiguring ? @MC9000 在 OnModelCreating 旁边的 DbContext 文件中【参考方案2】:

在 ASPNET Core 中,您可以在 Startup.cs 中执行此操作

public void ConfigureServices(IServiceCollection services)

    services.AddDbContext<BloggingContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));

你的连接在appsettings.json中定义


  "ConnectionStrings": 
    "BloggingDatabase": "..."
  ,

来自MS docs的示例

【讨论】:

你能检查一下这个***.com/questions/57000395/…我无法在appsettings.json中配置connectionString 如果我想在不同的项目中使用 DbContext,例如在 DA 层有一个单独的项目时怎么办?【参考方案3】:

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

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

所以,在 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 所需要的。

【讨论】:

这太好了,非常感谢!我已经找了好几个小时了。 什么是 ChromeContext ? 我收到错误,使用 (var context = new pixelleContext()) return context.Specsheet.ToList(); @rahularyansharma 也许开始你自己的问题,并把这个链接?这样你的代码示例可以更彻底一点吗?在上下文中,您上面的代码 sn-p 不是很有帮助。在我的示例中,ChromeContext 是数据库上下文的名称 暂时解决了:***.com/questions/68099089/…【参考方案4】:

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

第 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

【讨论】:

我的情况和你一样,我试过这个。但是,ConfigurationBuilder 没有方法“SetBasePath”。我怀疑这是因为我的 DLL 使用的是 Standard 而不是 Core。【参考方案5】:

将它作为 dp 注入传递给该类怎么样? 在配置服务中:

services.Configure<MyOptions>(Configuration);

创建类来保存 json 字符串:

public class MyOptions

    public MyOptions()
    

    
    public string Option1  get; set; 
    public string Option2  get; set; 
    

将字符串添加到 json 文件中:

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

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

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
 

【讨论】:

我看不出这是如何解决问题的。您能否编辑答案并将您的解决方案集成到问题示例中。 @borisdj 你怎么看不出它为 OP 的问题提供了解决方案? @JohanHerstad 我已经尝试过您的解决方案,这是一个好主意,但我认为因为 IDesignTimeDbContextFactory 在设计时而不是运行时被调用,所以您不能将 IOptions 注入到构造函数中,您可以在此处看到我的问题,进一步解释:***.com/questions/46085500/… Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations 中创建IDesignTimeDbContextFactory 的代码使用Activator,因此它需要一个无参数的构造函数。 是的,当然。我知道我已经接触过它,但当时我无法理解,也没有时间真正研究它。我只知道我必须在将来的某个时候也实施它,永远不会忘记这个问题。我很高兴你在第一个问题被问到之后这么长时间才花时间在下面给出答案。【参考方案6】:

实际上,您可以使用一种默认模式来实现此结果,而无需实现 IDesignTimeDbContextFactory 并进行任何配置文件复制。

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

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

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

在添加迁移时,当框架尝试创建您的 DbContext 时,它 first 会将它找到的任何 IDesignTimeDbContextFactory 实现添加到可用于创建上下文的工厂方法集合中,然后它会获取您的通过前面讨论过的静态钩子和looks 为使用DbContextOptions 注册的任何上下文类型配置服务(当您使用AddDbContextAddDbContextPool 时,这发生在您的Startup.ConfigureServices 中)并添加这些工厂。最后,它通过程序集looks 为任何DbContext 派生类创建一个工厂方法,该方法只调用Activator.CreateInstance 作为最后的冰雹玛丽。

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

【讨论】:

如果我在单独的项目中有DbContext,它将无法工作。【参考方案7】:

您也可以在 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));

【讨论】:

【参考方案8】:

    将以下代码添加到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
    
    

    Context 类中使用GlobalProperties.DBConnection 属性。

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

【讨论】:

【参考方案9】:

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

对于 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
    

所以现在你只需在你需要的任何其他类中使用静态的Program.Configuration

【讨论】:

【参考方案10】:

如果你需要在不同的层:

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

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;
        
    

【讨论】:

以上是关于从 appsettings.json 获取 ConnectionString,而不是在 .NET Core 2.0 App 中硬编码的主要内容,如果未能解决你的问题,请参考以下文章

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

如何在使用 .NET Core 的控制台应用程序中从 appsettings.json 获取值?

.NET Core appsettings.json 获取数据库连接字符串

从不同项目访问 appsettings.json

从 appsettings.json 读取动态参数

无法从类库访问 appsettings.json