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


配置User Secrets

ABP踩坑記錄-目錄

起因

因為以往習慣在User Secrets中保存連接字符串之類信息,但當我把連接字符串移到secrets.json中后,卻發現在遷移過程中會報如下的錯誤:

遷移報錯

簡單說,也就是遷移時無法獲取到連接字符串信息。

解決方案

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

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

經歷

這個問題看似解決容易,但還是費了我不少時間才找到原因。

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

所以,我開始在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項目中,這是導致問題的關鍵。

然后,我們來研究一下AppConfigurations類中的這段代碼:

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下的程序集。

也就是說,這里添加的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);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM