我正在为Azure构建一个WebJob,以便使用.Net Core 3.1在应用程序服务中运行。WebJob将通过Timers触发(它基本上是一个cronjob(。
计时器触发器需要AzureWebJobsStorage
连接字符串,因为计时器事件需要存储。
当部署到Azure应用程序服务时,我希望WebJob从应用程序服务的属性中读取AzureWebJobsStorage
值。
我有一个资源管理器模板,用于部署我的基础设施并在我的应用程序服务资源上设置连接字符串:
"connectionStrings": [
{
"name": "AzureWebJobsStorage",
"value": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('_StoreAccountName'), ';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('_StoreAccountName')), '2019-04-01').keys[0].value,';EndpointSuffix=core.windows.net')]"
}
],
在本地测试WebJob时,我需要设置AzureWebJobsStorage
值,以便本地构建可以连接到存储。
由于在对基础结构进行调整和更改时,我一直在重新部署它,所以我不想手动维护appsettings.json
或local.settings.json
文件中的长连接字符串。
在Visual Studio中,理论上,我可以为Azure Storage的项目添加服务依赖项,并将连接字符串存储在我的本地Secrets.json
文件中。然后,当我重新部署基础结构时,我可以使用Visual Studio UI编辑连接,并将其重新连接到新部署的存储帐户(即,它将创建和更新连接字符串,而无需我手动执行(。
当我将Azure存储添加为连接的服务时,Visual Studio在我的Secrets.json
文件中添加了一行类似的内容:
"ConnectionStrings:<LABEL>": "DefaultEndpointsProtocol=https;AccountName=<LABEL>;AccountKey=_____________;BlobEndpoint=https://<LABEL>.blob.core.windows.net/;TableEndpoint=https://<LABEL>.table.core.windows.net/;QueueEndpoint=https://<LABEL>.queue.core.windows.net/;FileEndpoint=https://<LABEL>.file.core.windows.net/",
在我的ServiceDependencies/serviceDependencies.local.json
:中
"storage1": {
"resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.Storage/storageAccounts/<LABEL>",
"type": "storage.azure",
"connectionId": "<LABEL>",
"secretStore": "LocalSecretsFile"
}
在我的ServiceDependencies/serviceDependencies.json
:中
"storage1": {
"type": "storage",
"connectionId": "<LABEL>"
}
其中,<LABEL>
是存储帐户的名称(在两个JSON窗口中(。
当我在本地运行WebJob时,它会将appsettings.json
、appsettings.Development.json
、secrets.json
和环境变量加载到IConfiguration中。
然而,当我在本地运行WebJob时,它会随着而消亡
Microsoft.Azure.WebJobs.Host.Listeners.FunctionListenerException: The listener for function 'Functions.Run' was unable to start.
---> System.ArgumentNullException: Value cannot be null. (Parameter 'connectionString')
at Microsoft.Azure.Storage.CloudStorageAccount.Parse(String connectionString)
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.get_TimerStatusDirectory() in C:azure-webjobs-sdk-extensionssrcWebJobs.ExtensionsExtensionsTimersSchedulingStorageScheduleMonitor.cs:line 77
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusBlobReference(String timerName) in C:azure-webjobs-sdk-extensionssrcWebJobs.ExtensionsExtensionsTimersSchedulingStorageScheduleMonitor.cs:line 144
at Microsoft.Azure.WebJobs.Extensions.Timers.StorageScheduleMonitor.GetStatusAsync(String timerName) in C:azure-webjobs-sdk-extensionssrcWebJobs.ExtensionsExtensionsTimersSchedulingStorageScheduleMonitor.cs:line 93
at Microsoft.Azure.WebJobs.Extensions.Timers.Listeners.TimerListener.StartAsync(CancellationToken cancellationToken) in C:azure-webjobs-sdk-extensionssrcWebJobs.ExtensionsExtensionsTimersListenerTimerListener.cs:line 99
at Microsoft.Azure.WebJobs.Host.Listeners.SingletonListener.StartAsync(CancellationToken cancellationToken) in C:projectsazure-webjobs-sdk-rqm4tsrcMicrosoft.Azure.WebJobs.HostSingletonSingletonListener.cs:line 72
at Microsoft.Azure.WebJobs.Host.Listeners.FunctionListener.StartAsync(CancellationToken cancellationToken, Boolean allowRetry) in C:projectsazure-webjobs-sdk-rqm4tsrcMicrosoft.Azure.WebJobs.HostListenersFunctionListener.cs:line 69
我已经确认,如果我将ConnectionStrings:AzureWebJobsStorage
值添加到我的appsettings.json
中,那么程序运行良好。
所以我知道AzureWebJobsStorage
值的加载有问题。
有人知道如何让本地运行的Azure WebJob正确读取Visual Studio在将Azure存储添加为连接服务时配置的连接字符串吗
如果Connected Service不读取连接字符串,那么将其添加到WebJob有什么意义?
(注:我意识到WebJobs文档https://learn.microsoft.com/en-us/azure/app-service/webjobs-sdk-how-to#webjobs-sdk版本声明Because version 3.x uses the default .NET Core configuration APIs, there is no API to change connection string names.
,但我不清楚这是否意味着底层WebJobs代码也拒绝在Connected Services设置中查找,或者我只是缺少了什么(
我找到了一个变通方法,但我不喜欢它……基本上是检查我的ConfigureAppConfiguration
代码末尾是否有ConnectionStrings:AzureWebJobsStorage
值,如果没有,请尝试从secrets.json
文件中读取该值,并将ConnectionStrings:AzureWebJobsStorage
设置为该值。
private const string baseAppSettingsFilename = "appsettings.json";
private const string defaultStorageAccountName = "<LABEL>";
...
IHostBuilder builder = new HostBuilder();
...
builder.ConfigureAppConfiguration(c =>
{
c.AddJsonFile(
path: baseAppSettingsFilename.Replace(".json", $".{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json"),
optional: true,
reloadOnChange: true);
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
{
c.AddUserSecrets<Program>();
}
// Add Environment Variables even though they are already added because we want them to take priority over anything set in JSON files
c.AddEnvironmentVariables();
IConfiguration config = c.Build();
if (string.IsNullOrWhiteSpace(config["ConnectionStrings:AzureWebJobsStorage"]))
{
string storageConnectionString = config[$"ConnectionStrings:{defaultStorageAccountName}"];
if (string.IsNullOrWhiteSpace(storageConnectionString))
{
throw new ConfigurationErrorsException($"Could not find a ConnectionString for Azure Storage account in ConnectionStrings:AzureWebJobsStorage or ConnectionStrings:{defaultStorageAccountName}");
}
c.AddInMemoryCollection(new Dictionary<string, string>() {
{ "ConnectionStrings:AzureWebJobsStorage", storageConnectionString }
});
}
});
这似乎非常愚蠢,但即使查看Azure SDK源代码,我也认为它只是硬编码为单个密钥名称,Visual Studio中的服务配置根本不受支持:https://github.com/Azure/azure-webjobs-sdk-extensions/blob/afb81d66749eb7bc93ef71c7304abfee8dbed875/src/WebJobs.Extensions/Extensions/Timers/Scheduling/StorageScheduleMonitor.cs#L77
我刚刚遇到一个类似的问题,VS2019自动配置了Function和Function1,Connection=";ConnectionStrings:AzureWebJobsStorage";但它找不到。简单地将其改变为Connection=";AzureWebJobsStorage";工作起来很有魅力。
仅供参考-我还不得不将BlobTrigger("Path/{name}"…更改为BlobTriger("Path/{name}";。。。re:Microsoft.Azure.StorageException:指定的资源名称包含无效字符