我已经用EF Core设置了一个ASP.NET Core 3.1项目,该项目在我的开发环境(Visual Studio 2019 Community,16.10.3版(中运行良好。对于我的数据库,我已经将连接字符串保存在secrets.json
中,一切都很好。
我现在开始为生产部署我的项目。我已经将机密移动到Azure密钥库,并根据Microsoft的文档使用Visual Studio Connected Services将密钥库添加到您的web应用程序中,使用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应用程序配置为连接到密钥库,并创建并分配一个托管身份:
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
如果我开始调试我的Web应用程序F5
,并在var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault"));
的Program.cs
文件中设置断点,我就可以访问存储在Azure密钥库中的机密。一切都很好。
然后,我尝试使用以下命令将数据库从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 Filesdotnetdotnet.exe exec --depsfile "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivacebinDebugnetcoreapp3.1Vivace.deps.json" --additionalprobingpath C:UsersMARCO.nugetpackages --additionalprobingpath "C:Program Files (x86)Microsoft Visual StudioSharedNuGetPackages" --additionalprobingpath "C:Program Files (x86)MicrosoftXamarinNuGet" --additionalprobingpath "C:Program FilesdotnetsdkNuGetFallbackFolder" --runtimeconfig "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivacebinDebugnetcoreapp3.1Vivace.runtimeconfig.json" C:UsersMARCO.nugetpackagesmicrosoft.entityframeworkcore.tools3.1.13toolsnetcoreapp2.0anyef.dll database update VivaceVereinUsers --context ApplicationDbContext --verbose --no-color --prefix-output --assembly "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivacebinDebugnetcoreapp3.1Vivace.dll" --startup-assembly "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivacebinDebugnetcoreapp3.1Vivace.dll" --project-dir "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivace\" --language C# --working-dir "C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburg" --root-namespace Vivace
Using assembly 'Vivace'.
Using startup assembly 'Vivace'.
Using application base 'C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivacebinDebugnetcoreapp3.1'.
Using working directory 'C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivace'.
Using root namespace 'Vivace'.
Using project directory 'C:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivace'.
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:UsersMARCODocumentsVisual Studio 2019ProjectsVivaceFreiburgVivaceProgram.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:line 24是以下代码行:
var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("mykeyvault"));
在错误中,我还读到Parameter 'uriString'
为空,但这是错误的。对于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/"
}
程序.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的值。