ABP框架入門踩坑-配置User Secrets

配置User Secrets

ABP踩坑記錄-目錄html

原由

由於以往習慣在User Secrets中保存鏈接字符串之類信息,但當我把鏈接字符串移到secrets.json中後,卻發如今遷移過程當中會報以下的錯誤:shell

遷移報錯

簡單說,也就是遷移時沒法獲取到鏈接字符串信息。數據庫

解決方案

  1. Qincai.EntityFrameworkCore項目中,找到QincaiDbContextFactory.cs文件,修改以下注釋處代碼。json

    public class QincaiDbContextFactory : IDesignTimeDbContextFactory<QincaiDbContext>
    {
        public QincaiDbContext CreateDbContext(string[] args)
        {
            var builder = new DbContextOptionsBuilder<QincaiDbContext>();
            //var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder());
            var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder(), addUserSecrets: true);
    
            QincaiDbContextConfigurer.Configure(builder, configuration.GetConnectionString(QincaiConsts.ConnectionStringName));
    
            return new QincaiDbContext(builder.Options);
        }
    }

經歷

這個問題看似解決容易,但仍是費了我很多時間才找到緣由。app

首先,我懷疑是否是secrets.json文件有問題,就把Qincai.Web.Host.csproj中的UserSecretsId刪掉,從新生成了一個。但難受的是,這下不單是遷移有問題了,連應用都沒法啓動了,固然報錯信息也是沒法找到鏈接字符串。ide

因此,我開始在StartUp中找注入配置的代碼,而後發現不一樣於微軟模板代碼中直接注入IConfiguration對象的作法,Module Zero本身實現了一個拓展方法IHostingEnvironment.GetAppConfiguration。其源碼以下:工具

public static IConfigurationRoot GetAppConfiguration(this IHostingEnvironment env)
{
    // 這裏第三個參數表明是否添加User Secrets
    // 能夠看到Module Zero默認只在開發環境中添加
    return AppConfigurations.Get(env.ContentRootPath, env.EnvironmentName, env.IsDevelopment());
}

這裏調用了AppConfigurations類,注意這個類是定義在Qincai.Core項目中,這是致使問題的關鍵。ui

而後,咱們來研究一下AppConfigurations類中的這段代碼:this

private static IConfigurationRoot BuildConfiguration(string path, string environmentName = null, bool addUserSecrets = false)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(path)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

    if (!environmentName.IsNullOrWhiteSpace())
    {
        builder = builder.AddJsonFile($"appsettings.{environmentName}.json", optional: true);
    }

    builder = builder.AddEnvironmentVariables();

    if (addUserSecrets)
    {
        // 在這裏添加了User Secrets
        builder.AddUserSecrets(typeof(AppConfigurations).GetAssembly());
    }

    return builder.Build();
}

乍一看好像沒問題,可是注意,我剛纔說了AppConfigurations是屬於Qincai.Core項目,即根據默認的配置,typeof(AppConfigurations).GetAssembly()獲取到的是Qincai.Core.dll下的程序集。spa

也就是說,這裏添加的User Secrets是根據Qincai.Core.csproj中定義的UserSecretsId,而不是Qincai.Web.Host.csproj中定義的。

打開Qincai.Core.csproj,咱們確實注意到有一個UserSecretsId字段,且和Qincai.Web.Host.csproj中未修改器前的一致。

這裏就是Module Zero取巧的地方了,由於在VS中,只有Web項目才能在右鍵中找到管理用戶機密,因此經過兩個配置兩個相同的UserSecretsId,使其能經過VS的快捷方式修改Qincai.Core項目的secrets.json文件。

既然找到緣由了,那麼只需將UserSecretsId恢復成一致便可,痛快地敲下F5,啓動成功。

所以注意,一旦在你修改了Qincai.Web.Host.csproj中的UserSecretsId後,千萬不要忘了修改Qincai.Core.csproj,務必確保兩個UserSecretsId一致,否則你再怎麼改,程序也是屆不到的。


不是完了嗎?纔怪嘞!!咱們的問題是遷移時沒法讀取User Secrets,以上經歷只能說又回到了起點。

到這裏,咱們已經能夠在程序運行時成功讀取到User Secrets,可是在數據庫遷移過程當中,仍是會報錯:

System.ArgumentNullException: Value cannot be null.
Parameter name: connectionString

讓咱們打開ef工具的詳細輸出-v來看一看,其中有這麼一段輸出:

Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider...
Finding IWebHost accessor...
Using environment 'Development'.
Using application service provider from IWebHost accessor on 'Program'.
Finding DbContext classes in the project...
Found DbContext 'QincaiDbContext'.
Using DbContext factory 'QincaiDbContextFactory'.

它提到了QincaiDbContextFactory這個類,源碼以下:

/* This class is needed to run "dotnet ef ..." commands from command line on development. Not used anywhere else */
public class QincaiDbContextFactory : IDesignTimeDbContextFactory<QincaiDbContext>
{
    public QincaiDbContext CreateDbContext(string[] args)
    {
        var builder = new DbContextOptionsBuilder<QincaiDbContext>();
        // 注意這裏
        var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder();

        QincaiDbContextConfigurer.Configure(builder, configuration.GetConnectionString(QincaiConsts.ConnectionStringName));

        return new QincaiDbContext(builder.Options);
    }
}

看到註釋,咱們應該就是找對地方了,注意我在代碼中標出的位置。它也經過AppConfigurations.Get來獲取配置,可是沒有給出AddUserSecrets參數(默認爲false),而根據此前的代碼可知,它沒有添加User Secrets。

那麼解決方案就很簡單了,顯式給出AddUserSecrets參數便可。

var configuration = AppConfigurations.Get(WebContentDirectoryFinder.CalculateContentRootFolder(), addUserSecrets: true);
相關文章
相關標籤/搜索