从appsettings.json获取ConnectionString而不是在.NET Core 2.0应用程序中硬编码

104

我在 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);
    }
}

如果在运行update-database时,数据库不存在且需要创建,则在Core 2.0中需要此项操作。
升级到ASP.NET Core 2.0后无法创建迁移

我希望不要在这里和appsettings.json两个地方都有ConnectionString,而只在.json文件中存在,所以我尝试替换了

"Server=localhost;Database=DbName;Trusted_Connection=True;MultipleActiveResultSets=true"
with
ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString
但它不起作用,我得到了空值。
更新1:
只需注意,在Core 2中明确添加.json是不必要的,因此问题不在文件中。
https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/

更新2:
此外,我已经在使用Configuration从.json向上下文(Context)发送ConnectionString。
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中使用这个,因为它没有 Configuration,而且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中。您是否已经这样做了?'普通'应用程序设置是否正常工作? - Jan_V
1
在Core2.0中,这是自动完成的:https://andrewlock.net/exploring-program-and-startup-in-asp-net-core-2-preview1-2/ - borisdj
在Core2.0中,您可以使用System.AppContext.BaseDirectory来获取基本路径,如果您无法在启动时执行此操作,则可以参考@borisdj的建议:https://github.com/aspnet/Announcements/issues/237。 - William Ardila
3
对于那些想知道SetBasePath来自哪里的人:https://dev59.com/HVYN5IYBdhLWcg3w07Fx#46843572;而`AddJsonFile`来自哪里:https://dev59.com/sF4c5IYBdhLWcg3w7t7E - Adam Houldsworth
谢谢!我认为您应该发布一个单独的答案,而不是将解决方案嵌入问题中。 - Shimmy Weitzhandler
13个回答

152

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

第二步:创建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 获取)。


8
我不同意!将连接字符串放在 appsettings.json 中复制一份是和在两个地方都声明连接字符串一样糟糕!谁来负责确保它们同步? - Marcel
2
问题的背景是,我和borisdj都遇到了在.NET Core 2.0中System.Configuration不再存在并需要被替换的情况。为了让其他人更容易理解我的答案,我选择了第三步的纸质方法。但你当然是正确的,在实际情况下,你会将"appsetting.json"添加到你的项目中,并在"属性页"的"常规-高级"选项卡中设置"复制到输出目录"为"始终复制",之后就不需要手动复制操作了。 - JRB
2
如果您想从自定义部分的 appsettings.json 获取连接字符串,请查看此链接 https://dev59.com/yek5XIcBkEYKwwoY9-lg#53924899 - Dmitry Pavlov
1
我在哪里找到 OnConfiguring? - MC9000
1
在您的 DbContext 文件中,在 OnModelCreating 旁边加上 @MC9000。 - Janneck Lange
显示剩余3条评论

24
在ASP.NET Core中,您需要在Startup.cs中完成它。
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<BloggingContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("BloggingDatabase")));
}

你的连接在appsettings.json中被定义

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

来自 MS文档的示例


请问您能否检查一下这个链接 https://stackoverflow.com/questions/57000395/get-connectionstring-from-appsettings-json 我无法在appsettings.json中配置connectionString。 - Gopi
3
如果我想在另一个项目中使用DbContext,例如在将DA层单独放在一个项目中时,该怎么办? - Chris

15

以上的解决方案以及 Microsoft 文档中都缺少一些要点。如果你点击上述文档中链接到 GitHub 存储库,你会找到真正的解决方案。

我认为困惑在于许多人使用的默认模板并没有包含 Startup 的默认构造函数,因此人们不知道注入的 Configuration 是从哪里来的。

所以,在 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标准2.1项目中使用Entity Framework Core 3测试过的。 我需要添加以下NuGet包: Microsoft.EntityFrameworkCore 3.0.0 Microsoft.EntityFrameworkCore.SqlServer 3.0.0,因为这是我正在使用的内容,并且需要访问UseSqlServer。


太好了,非常感谢!我已经寻找这个东西几个小时了。 - dazoar
什么是ChromeContext? - rahularyansharma
我遇到了错误,使用(var context = new pixelleContext()) { 返回context.Specsheet.ToList(); } - rahularyansharma
1
@rahularyansharma 也许你可以发起自己的问题并放置一个链接到这个问题?这样你就可以更加详细地展示你的代码示例了。在这种情况下,你上面的代码片段并不是很有帮助。在我的示例中,ChromeContext 是数据库上下文的名称。 - Kevon
目前问题已解决:https://stackoverflow.com/questions/68099089/what-changes-need-to-be-made-in-context-class-to-move-connection-string-into-app/68108901#68108901 - rahularyansharma

9
我知道这个问题已被标记为已回答,但是在我处理一个项目时遇到了一些问题。我的 EF Core 数据访问层 在一个 .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能够智能地选择正确的文件,从而无需将文件复制到目录中。同时,指定的环境将在构建发布或生产时选择正确的文件,假设已选择了生产环境文件。

步骤2:创建appsettings.json

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

请参考:Microsoft.Extensions.Configuration


我和你处于同样的情况,我尝试了这个方法。但是,ConfigurationBuilder没有"SetBasePath"方法。我怀疑这是因为我使用的是标准版DLL而不是核心版。 - EGP
1
尝试使用Microsoft.Extensions.Configurations.Json包。 - Shyam Dixit

6
如果您正在使用“顶级语句”,则可以按以下方式获取连接字符串:
```csharp var connectionString = "your_connection_string"; ```
var builder = WebApplication.CreateBuilder(args);

var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");

5
在 ConfigureServices 中将其作为依赖项注入到该类中,怎么样?
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
 }
}

1
我不明白这个如何解决问题。你能否编辑答案,并将你的解决方案融入到问题的示例中? - borisdj
1
@borisdj,你怎么看不出来它提供了一种解决方案来解决问题呢? - Brian Ogden
1
@JohanHerstad 我尝试了你的解决方案,是个好主意,但我认为因为IDesignTimeDbContextFactory<AppContext>在设计时而不是运行时被调用,所以你不能将IOptions注入到构造函数中。你可以在这里看到我的问题进一步解释:https://dev59.com/yek5XIcBkEYKwwoY9-lg - Brian Ogden
1
Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations中负责创建IDesignTimeDbContextFactory的代码使用了Activator,因此需要一个无参构造函数。 - Erikest
当然可以。我知道我曾经接触过它,但当时我无法理解,并且没有时间进行深入研究。我只知道将来某个时候也必须实现它,从未忘记这个问题。我很高兴您花时间在第一个问题被问出很久之后给出了答案。 - Johan Herstad

4

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

这在this doc中详细介绍,该文档还讨论了框架在设计时尝试实例化DbContext的其他方式。

具体来说,您需要利用一个新的钩子,即形式为public static IWebHost BuildWebHost(string[] args)的静态方法。文档暗示了另外一种方式,但是这个方法可以存在于任何包含入口点的类中(请参见src)。实现这一点是1.x到2.x迁移文档中的指导的一部分,而且在查看代码时不完全明显的是,对WebHost.CreateDefaultBuilder(args)的调用是连接默认模式下新项目开始的配置等其他内容的一部分。这就是您需要做的一切,以便让配置被设计时间服务(如迁移)使用。

以下是更深层次的细节:

在添加迁移时,当框架尝试创建您的DbContext时,它first会将其找到的任何IDesignTimeDbContextFactory实现添加到可用于创建上下文的工厂方法集合中,然后通过前面讨论的静态钩子获取已配置的服务,并looks寻找使用AddDbContextAddDbContextPool注册了DbContextOptions的任何上下文类型并添加这些工厂。最后,它looks浏览程序集以查找任何派生自DbContext的类,并创建一个只调用Activator.CreateInstance的工厂方法作为最后的备选方案。
框架使用的优先顺序与上述相同。因此,如果您实现了IDesignTimeDbContextFactory,它将覆盖上面提到的钩子。但对于大多数常见情况,您不需要IDesignTimeDbContextFactory

2
如果我将DbContext放在一个单独的项目中,它就无法工作。 - Konrad

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

1

只需使用

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

1

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接