尝试使用 Update-Database -Migration 命令迁移到 Azure SQL 数据库时,Azure keyVaultEndpoint return null

Azure keyVaultEndpoint return null while trying migration to Azure SQL Database with Update-Database -Migration command

我已经使用 EF Core 设置了一个 ASP.NET Core 3.1 项目,在我的开发环境中 运行 没问题(Visual Studio 2019 社区,版本 16.10.3) .对于我的数据库,我已将连接字符串保存在 secrets.json 中,一切正常。

我现在开始部署我的生产项目。 我已将机密移至 Azure Key Vault 并根据 Microsoft 的文档 Add Key Vault to your web application by using Visual Studio Connected Services.

使用 Connected services 进行设置

在我的 Program.cs 文件中,我现在有了获取 keyVaultEndpoint.

的代码
public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault"));
                
                config.AddAzureKeyVault(keyVaultEndpoint, new DefaultAzureCredential());
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

我的 launchSettings.json 文件是:

{
    "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:54964",
      "sslPort": 44331
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "mykeyvault": "https://<mykeyvault>.vault.azure.net/",
        "uriString": "https://<mykeyvault>.vault.azure.net/",
        "AZURE_USERNAME": "myAzureUsername",
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "Vivace": {
      "commandName": "Project",
      "launchBrowser": true,
      "environmentVariables": {
        "mykeyvault": "https://<mykeyvault>.vault.azure.net/",
        "uriString": "https://<mykeyvault>.vault.azure.net/",
        "AZURE_USERNAME": "myAzureUsername",
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "applicationUrl": "https://localhost:5001;http://localhost:5000"
    }
  }
}

然后我将数据库连接字符串移动到 Azure。 我将 Web 应用配置为连接到 Key Vault,并创建并分配了一个托管标识:

az webapp identity assign --name "<my-webapp-name>" --resource-group "myResourceGroup"

为了授予我的 Web 应用程序在我的密钥保管库上执行获取和列出操作的权限,我将 principalId 传递给了 Azure CLI az keyvault set-policy 命令:

az keyvault set-policy --name "<mykeyvault>" --object-id "<principalId>" --secret-permissions get list

如果我开始调试 F5 我的 webapp 并在 Program.cs 文件中的 var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault")); 处设置断点,我就可以访问存储在 Azure Key Vault 中的机密。一切正常。

然后我尝试使用以下命令将数据库从 Package Manager Console 迁移到 Azure SQL:

Update-Database -Migration VivaceVereinUsers -Context ApplicationDbContext -Verbose

我添加了 -Verbose 以更好地理解发生了什么。

这是我遇到的错误:

    Using project 'Vivace'.
Using startup project 'Vivace'.
Build started...
Build succeeded.
C:\Program Files\dotnet\dotnet.exe exec --depsfile "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\bin\Debug\netcoreapp3.1\Vivace.deps.json" --additionalprobingpath C:\Users\MARCO\.nuget\packages --additionalprobingpath "C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages" --additionalprobingpath "C:\Program Files (x86)\Microsoft\Xamarin\NuGet" --additionalprobingpath "C:\Program Files\dotnet\sdk\NuGetFallbackFolder" --runtimeconfig "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\bin\Debug\netcoreapp3.1\Vivace.runtimeconfig.json" C:\Users\MARCO\.nuget\packages\microsoft.entityframeworkcore.tools.1.13\tools\netcoreapp2.0\any\ef.dll database update VivaceVereinUsers --context ApplicationDbContext --verbose --no-color --prefix-output --assembly "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\bin\Debug\netcoreapp3.1\Vivace.dll" --startup-assembly "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\bin\Debug\netcoreapp3.1\Vivace.dll" --project-dir "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\" --language C# --working-dir "C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg" --root-namespace Vivace
Using assembly 'Vivace'.
Using startup assembly 'Vivace'.
Using application base 'C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\bin\Debug\netcoreapp3.1'.
Using working directory 'C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace'.
Using root namespace 'Vivace'.
Using project directory 'C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\'.
Finding DbContext classes...
Finding IDesignTimeDbContextFactory implementations...
Finding application service provider...
Finding Microsoft.Extensions.Hosting service provider...
Using environment 'Development'.
System.ArgumentNullException: Value cannot be null. (Parameter 'uriString')
   at System.Uri..ctor(String uriString)
   at Vivace.Program.<>c.<CreateHostBuilder>b__1_0(HostBuilderContext context, IConfigurationBuilder config) in C:\Users\MARCO\Documents\Visual Studio 2019\Projects\VivaceFreiburg\Vivace\Program.cs:line 24
   at Microsoft.Extensions.Hosting.HostBuilder.BuildAppConfiguration()
   at Microsoft.Extensions.Hosting.HostBuilder.Build()
An error occurred while accessing the Microsoft.Extensions.Hosting services. Continuing without the application service provider. Error: Value cannot be null. (Parameter 'uriString')
No application service provider was found.
Finding DbContext classes in the project...
Found DbContext 'ApplicationDbContext'.
Found DbContext 'FilippaLibraryContext'.
Found DbContext 'VivaceCoursesContext'.
Microsoft.EntityFrameworkCore.Design.OperationException: Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728
 ---> System.MissingMethodException: No parameterless constructor defined for type 'Vivace.Data.ApplicationDbContext'.
   at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass13_3.<FindContextTypes>b__13()
   --- End of inner exception stack trace ---
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.<>c__DisplayClass13_3.<FindContextTypes>b__13()
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String contextType)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Unable to create an object of type 'ApplicationDbContext'. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

Program.cs:第24行是下面的代码行:

var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault"));

在错误中我还读到 Parameter 'uriString' 为空,但这是错误的。 Il launchSettings.json uriString 已正确设置。请参考上面的附件代码。

我发现将我的数据库迁移到 Azure SQL 的唯一方法是替换代码行

var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault"));

与:

var keyVaultEndpoint = new Uri("https://<mykeyvault>.vault.azure.net/");

似乎由于某些原因,Package Manager Console 中的 运行 Update-Database -Migration 阻止访问 EnvironmentVariable

我还交叉检查了部署是否完成。 我可以使用 VS 2019 中的 SQL Server Object Explorer 查看包含所有表(仍然为空)的数据库。 我已将项目部署到 http://myproject.azurewebsites.net 并导航到显示数据库项目的页面。该页面是空的,但我没有收到任何错误。

知道错误吗? 我有一个解决方法,但它离最佳实践还很远。我不想评论和取消注释代码行以将我的数据库迁移到 Azure。它一定能正常工作!

launchSettings.json是本地开发机特有的,构建发布时不部署。因此,您的代码无法检索值“mykeyvault”。

推荐的方法是将配置移动到 appsettings.json,以便它包含在构建中并在您部署应用程序时发布。

appsettings.json

{
  "mykeyvault": "https://<mykeyvault>.vault.azure.net/"
}

Program.cs

public class Program
    {
        private static IConfigurationRoot _configuration;

        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((context, config) =>
            {
                _configuration = config.Build();
                var keyVaultEndpoint = new Uri(_configuration["mykeyvault"]);
                config.AddAzureKeyVault(keyVaultEndpoint, new DefaultAzureCredential());
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });

您可以根据要从 DevOps 管道部署到的环境来设置 mykeyvault 的值。