Windows Azure - 如何解決"SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used”的問題
其實這個問題,主要是來自Windows Azure 1.2升級到1.3時發生的問題,主要的原因是因為Windows Azure 1.2所使用的iis機制和Windows Azure 1.3的iis不同的關係;雖然目前已經到了1.6 ( 搞不好過幾天又更新了 ),但只要使用到Windows Azure Storage,還是有可能會發生;其次,也因為這樣的改變,所以之前小弟也一直搞不清楚這些的因果關係,查了一些資料後,小弟還是在這邊做個紀錄。
Windows Azure 1.2時代
Windows Azure 1.2時,大家取得Windows Azure Storage會利用下面方式做取得,先利用SetConfigurationSettingPublisher設定連線字串,然後在真正要用到的地方,利用FromConfigurationSetting來取得Account。( FromConfigurationSetting方法的功用就是會利用SetConfigurationSettingPublisher設定好的資訊來傳會一個Account物件。 ),所以會這樣寫。
在WebRole.cs的onStart方法裡面,會這樣寫。( 這個WebRole.cs檔案,會控制Role的生命週期與管理,所以我們建立一個Cloud專案的時候,其實都會默默的幫我們加上這個Class在底下,而早期1.2的時代,我們會利用WebRole.cs裡面的OnStart方法,來撰寫一些Role被啟動,或是被調用時,處理的一些事情,所以我們也會把SetConfigurationSettingPublisher寫在裡面。 )
而早期,我們會將SetConfigurationSettingPublisher寫在WebRole.cs裡面的OnStart方法。
(configName, configSettingPublisher) =>
{
var connectionString =
RoleEnvironment.GetConfigurationSettingValue(configName);
configSettingPublisher(connectionString);
}
);
然後在主要的網頁裡面( 例如Default.aspx.cs ), 想要取得Account,會這樣寫。
當然,1.2是沒有問題的,但是這在Windows Azure 1.3之後,就會出錯了。
Windows Azure 1.3時代
左圖是1.2時代的架構圖,當時的Web Role是運作在HWC之上。而右邊是1.3的架構圖,WebRole.cs其實是運作在WallShos.exe這塊,但是真正的網頁執行環境,卻是會落在w3wp.exe這邊,也就是真正完整的IIS。 ( 早期的1.2版本,並不是完整的IIS )。
其實,這也就是為什麼1.2升級到1.3的時候,會出現下面這個錯誤。
SetConfigurationSettingPublisher needs to be called before FromConfigurationSetting can be used
這個原因其實很簡單,因為我們是在WebRole.cs裡面撰寫SetConfigurationSettingPublisher,來設定連線字串,但我們卻在另外一個地方使用FromCofigurationSetting來產生Account,所以當然會出現"要使用FormConfigurationSetting之前,必須先呼叫SetConfigurationSettingPublisher"的錯誤。
大家的解決方法
通常大家的解決方法,就是把原本的WebRole.cs裡面SetConfigurationSettingPublisher這段程式碼搬到Global.asax裡的Application_Start底下。
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure;
namespace WebRole
{
public class Global : System.Web.HttpApplication
{
void Application_Start(object sender, EventArgs e)
{
// 應用程式啟動時執行的程式碼
//如果要使用var account = CloudStorageAccount.FromConfigurationSetting("TestConnectionStrting");
//來存取Account,則必須加入下面這些程式碼。
Microsoft.WindowsAzure.CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) =>
{
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
});
}
void Application_End(object sender, EventArgs e)
{
// 應用程式關閉時執行的程式碼
}
void Application_Error(object sender, EventArgs e)
{
// 發生未處理錯誤時執行的程式碼
}
void Session_Start(object sender, EventArgs e)
{
// 啟動新工作階段時執行的程式碼
}
void Session_End(object sender, EventArgs e)
{
// 工作階段結束時執行的程式碼。
// 注意: 只有在 Web.config 檔將 sessionstate 模式設定為 InProc 時,
// 才會引發 Session_End 事件。如果將工作階段模式設定為 StateServer
// 或 SQLServer,就不會引發這個事件。
}
}
}
其實看了上面的圖,就會了解,因為Global.asax和要使用Account的Default.aspx都是同一區阿!所以這樣調整後,就可以正確執行了。
另一種方法
其實如果沒有甚麼特別需求,小弟我也比較喜歡官方推薦的這種方法,這個方法結合了上面需要在各個地方設置的SetConfigurationSettingPublisher和FromConfigurationSetting,在需要的地方直接利用Parse來取得,算是最方便也是最推薦的了,可以看看下面程式碼。
RoleEnvironment.GetConfigurationSettingValue("MyConnectionString"))
基本上,大致上就是這樣了。
後記
因為這幾種方法,和錯誤訊息,是最容易在初學的時候搞不懂和發生的,像小弟第一次做的時候,常常就搞不懂,為什麼會有那麼多種寫法,但了解時空背景後,就會比較清楚這些的用法,所以小弟我也在這邊紀錄一下。
參考資料
- http://msdn.microsoft.com/en-us/library/microsoft.windowsazure.cloudstorageaccount.fromconfigurationsetting.aspx
- http://msdn.microsoft.com/en-us/library/gg433113.aspx
- http://blogs.msdn.com/b/cesardelatorre/archive/2011/03/05/how-to-solve-setconfigurationsettingpublisher-needs-to-be-called-before-fromconfigurationsetting-can-be-used-after-moving-to-windows-azure-sdk-1-3.aspx
- http://blogs.msdn.com/b/windowsazure/archive/2010/12/08/how-to-resolve-setconfigurationsettingpublisher-needs-to-be-called-before-fromconfigurationsetting-can-be-used-after-moving-to-windows-azure-sdk-1-3.aspx
- http://www.tuicool.com/articles/VbMfeu
- http://www.cnblogs.com/threestone/archive/2012/03/06/2380214.html
- http://ruddyleemsblog.wordpress.com/