【发布时间】:2023-03-23 05:25:01
【问题描述】:
我正在尝试在本地使用 Azure 存储。我有一个名为 ExpenseDataSource 的数据源类:
public class ExpenseDataSource
{
private static CloudStorageAccount storageAccount;
private ExpenseTableContext context;
static ExpenseDataSource()
{
//CloudStorageAccount.SetConfigurationSettingPublisher(
// (configName, configSettingPublisher) =>
// {
// string connectionString = RoleEnvironment.GetConfigurationSettingValue(configName);
// configSettingPublisher(connectionString);
// }
//);
storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
CloudTableClient.CreateTablesFromModel(typeof(ExpenseTableContext), storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
}
public ExpenseDataSource()
{
context = new ExpenseTableContext(storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
context.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
}
public IEnumerable<ExpenseInfo> Select()
{
var results = from g in context.Expenses
where g.PartitionKey == "Expense"
select g;
return results;
}
// ...
}
(我是 Azure 的新手,所以这个类在很多方面都不是最理想的。)
当我尝试创建ExpenseDataSource 类型的对象时,出现以下异常:
System.TypeInitializationException: The type initializer for 'WebRole1.ExpenseDataSource' threw an exception. ---> System.InvalidOperationException: SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used
at Microsoft.WindowsAzure.CloudStorageAccount.FromConfigurationSetting(String settingName)
at WebRole1.ExpenseDataSource..cctor() in [ ... ]
--- End of inner exception stack trace ---
at WebRole1.ExpenseDataSource..ctor()
at WebRole1.ExpenseService.WebRole1.IExpenseService.GetExpenses() in [ ... ]
但是,这很奇怪,因为 SetConfiguationSettingPublisher 已经被调用:
public class WebRole : RoleEntryPoint
{
public override bool OnStart()
{
DiagnosticMonitor.Start("DiagnosticsConnectionString");
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSettingPublisher) =>
{
string connectionString = RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
return base.OnStart();
}
// ...
}
当我开始调试时,我可以在这里打断点。
我在这里做错了什么?
更新:我想可能是我启动了 devfabric 和 ASP.NET localhost 故障,所以我将它们都杀了,启动了 dev fabic,然后启动了 ASP 项目。仍然没有运气 - 发生同样的错误。
更新 2:我将我的 OnStart() 更改为这个,但它仍然不起作用:
public override bool OnStart()
{
DiagnosticMonitor.Start("DiagnosticsConnectionString");
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
#region Setup CloudStorageAccount Configuration Setting Publisher
// This code sets up a handler to update CloudStorageAccount instances when their corresponding
// configuration settings change in the service configuration file.
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
// Provide the configSetter with the initial value
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
RoleEnvironment.Changed += (sender, arg) =>
{
if (arg.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Any((change) => (change.ConfigurationSettingName == configName)))
{
// The corresponding configuration setting has changed, propagate the value
if (!configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)))
{
// In this case, the change to the storage account credentials in the
// service configuration is significant enough that the role needs to be
// recycled in order to use the latest settings. (for example, the
// endpoint has changed)
RoleEnvironment.RequestRecycle();
}
}
};
});
#endregion
return base.OnStart();
}
更新 3:我尝试将“Setup CloudStorageAccount Configuration Setting Publisher”区域放在 ExpenseDataSource 静态初始化程序中,得到以下错误:
System.TypeInitializationException: The type initializer for 'WebRole1.ExpenseDataSource' threw an exception. ---> System.Runtime.InteropServices.SEHException: External component has thrown an exception.
at RoleEnvironmentGetConfigurationSettingValueW(UInt16* , UInt16* , UInt32 , UInt32* )
at Microsoft.WindowsAzure.ServiceRuntime.Internal.InteropRoleManager.GetConfigurationSetting(String name, String& ret)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue(String configurationSettingName)
at WebRole1.ExpenseDataSource.<.cctor>b__0(String configName, Func`2 configSetter) in C:\Users\ODP\Documents\Visual Studio 2010\Projects\ExpenseCalc\WebRole1\ExpenseDataSource.cs:line 26
at Microsoft.WindowsAzure.CloudStorageAccount.StorageAccountConfigurationSetting..ctor(String configurationSettingName)
at Microsoft.WindowsAzure.CloudStorageAccount.FromConfigurationSetting(String settingName)
at WebRole1.ExpenseDataSource..cctor() in C:\Users\ODP\Documents\Visual Studio 2010\Projects\ExpenseCalc\WebRole1\ExpenseDataSource.cs:line 47
--- End of inner exception stack trace ---
at WebRole1.ExpenseDataSource..ctor()
at WebRole1.ExpenseService.WebRole1.IExpenseService.GetExpenses() in C:\Users\ODP\Documents\Visual Studio 2010\Projects\ExpenseCalc\WebRole1\ExpenseService.svc.cs:line 18
更新 3:按照 smarx 的建议,我更改了静态初始化程序:
static ExpenseDataSource()
{
//storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
storageAccount = CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString"));
CloudTableClient.CreateTablesFromModel(typeof(ExpenseTableContext), storageAccount.TableEndpoint.AbsoluteUri, storageAccount.Credentials);
}
这会导致以下错误:
System.TypeInitializationException: The type initializer for 'WebRole1.ExpenseDataSource' threw an exception. ---> System.Runtime.InteropServices.SEHException: External component has thrown an exception.
at RoleEnvironmentGetConfigurationSettingValueW(UInt16* , UInt16* , UInt32 , UInt32* )
at Microsoft.WindowsAzure.ServiceRuntime.Internal.InteropRoleManager.GetConfigurationSetting(String name, String& ret)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetConfigurationSettingValue(String configurationSettingName)
at WebRole1.ExpenseDataSource..cctor() in C:\Users\ODP\Documents\Visual Studio 2010\Projects\ExpenseCalc\WebRole1\ExpenseDataSource.cs:line 20
--- End of inner exception stack trace ---
at WebRole1.ExpenseDataSource..ctor()
at WebRole1.ExpenseService.WebRole1.IExpenseService.GetExpenses() in C:\Users\ODP\Documents\Visual Studio 2010\Projects\ExpenseCalc\WebRole1\ExpenseService.svc.cs:line 18
错误与上面略有不同。这是否与我实际上并未在开发结构中运行 ASP.NET 的想法有关?
呃。我开始怀念 Google App Engine 存储的简单 get() 和 put() 界面。
【问题讨论】:
-
断点的命中顺序是否正确?
-
不确定。
OnStart()断点只有在我第一次启动 Azure 开发结构时才会受到影响。 (即使我取消附加调试器,我想这不会杀死角色,因此以后不会调用OnStart()。)我永远无法命中 WCF 服务或 @987654336 中设置的断点@. -
我有点困惑...您确定每次都启动云项目而不是 ASP.NET 项目吗? (您可以打开开发结构 UI 并验证您的应用程序是否正在运行。)听起来您可能正在开发结构 外部 运行 ASP.NET 项目。此外,您可以考虑使用 .Parse(RoleEnvironment.GetConfigurationSettingValue(...)) 而不是 .FromConfigurationSetting(...)。前者不需要设置发布者,但当您在运行时更改存储密钥时,它也不会自动修复问题。
-
@smarx 是的,听起来我可能在开发结构之外运行 ASP.NET 项目。我该如何验证/解决这个问题?
-
我似乎记得一些关于静态构造函数和配置设置的事情。这与调用事物的顺序有关。只是为了看看是否是那个问题,我会尝试将当前静态的内容移至实际的构造函数(我知道这不是一个好的长期解决方案)
标签: azure azure-storage