自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何在ASP.NET應(yīng)用程序中初始化

開(kāi)發(fā) 后端
今天給大家介紹了4種在ASP.NET中執(zhí)行初始化的方法,或許有些人會(huì)想:到底該選擇哪種初始化方法呢?的確,方法越多越讓人迷惑。

每個(gè)程序都需要初始化的過(guò)程,用來(lái)讀取配置或者設(shè)置一些運(yùn)行環(huán)境(變量),對(duì)于ASP.NET程序來(lái)說(shuō),又該在哪里執(zhí)行初始化的任務(wù)呢?

我想應(yīng)該絕大多數(shù)人都知道在Global.asax中執(zhí)行初始化的過(guò)程,然而有些細(xì)節(jié)是我們需要關(guān)注的。

本文用例

在這篇博客的示例代碼中,AppInitializer包含了網(wǎng)站的初始化的實(shí)現(xiàn)代碼:

  1. public static class AppInitializer  
  2. {  
  3.     public static ConnectionStringSettings MyNorthwindConnectionSetting { getprivate set; }  
  4.  
  5.     public static void Init()  
  6.     {  
  7.         // 讀取連接字符串。  
  8.         LoadConnectionString();  
  9.  
  10.         // 設(shè)置SQLSERVER緩存依賴通知。  
  11.         SetSqlDependency();  
  12.  
  13.         // 其它的初始化操作。  
  14.         OthersInit();  
  15.     }  
  16.  
  17.     static void LoadConnectionString()  
  18.     {  
  19.         ConnectionStringSettings setting = ConfigurationManager.ConnectionStrings["MyNorthwind"];  
  20.         if( setting == null )  
  21.             throw new ConfigurationException("沒(méi)有配置MyNorthwind連接字符串。");  
  22.  
  23.         ifstring.IsNullOrEmpty(setting.ConnectionString) )  
  24.             throw new ConfigurationException("沒(méi)有為MyNorthwind連接字符串指定內(nèi)容。");  
  25.  
  26.         ifstring.IsNullOrEmpty(setting.ProviderName) )  
  27.             throw new ConfigurationException("沒(méi)有為MyNorthwind連接字符串指定ProviderName 。");  
  28.  
  29.         // 保存讀取到的連接字符串,供程序使用。  
  30.         MyNorthwindConnectionSetting = setting;  
  31.     }  
  32.  
  33.     static void SetSqlDependency()  
  34.     {  
  35.         // 判斷SQLSERVER版本是否為 2005以上版本,  
  36.         // 是否開(kāi)啟Service Broker的檢查代碼就不列出了。  
  37.  
  38.         SqlDependency.Start(MyNorthwindConnectionSetting.ConnectionString);  
  39.     }  
  40.  
  41.     static void OthersInit()  
  42.     {  
  43.         // 其它的初始化操作。  
  44.  
  45.         // 例如:  
  46.         // 1. 加載必要的緩存數(shù)據(jù)。  
  47.         // 2. 檢查上傳目錄是不存在。  
  48.         // 3. ...................  
  49.     }  

這段代碼的意圖很清楚,一定要確保正確的配置了數(shù)據(jù)庫(kù)連接字符串,否則以異常的形式報(bào)告出來(lái)。

示例程序還有一個(gè)頁(yè)面,Default.aspx

  1. <body> 
  2.     <form id="form1" runat="server"> 
  3.     <div> 
  4.         <h1>User Login</h1> 
  5.     </div> 
  6.  
  7.         <p style="line-height: 150%;"> 
  8.             UserName: <asp:TextBox ID="txtUserName" runat="server" Width="200px" Text="Fish Li"></asp:TextBox><br /> 
  9.             Password: <asp:TextBox ID="txtPassword" runat="server" Width="200px" TextMode="Password"></asp:TextBox><br /> 
  10.             <asp:Button ID="btnLogin" runat="server" Text="登錄" OnClick="btnLogin_Click" /> 
  11.         </p> 
  12.     </form> 
  13. </body> 

其實(shí)就是一個(gè)登錄頁(yè)面,后臺(tái)代碼為:

  1. protected void btnLogin_Click(object sender, EventArgs e)  
  2. {  
  3.     bool ok = false;  
  4.  
  5.     using( SqlConnection connection  
  6.         = new SqlConnection(AppInitializer.MyNorthwindConnectionSetting.ConnectionString) ) {  
  7.  
  8.         connection.Open();  
  9.  
  10.         // 其它的數(shù)據(jù)庫(kù)操作。  
  11.  
  12.         ok = true;  
  13.     }  
  14.  
  15.     if( ok )  
  16.         Response.Redirect("Default2.aspx");  

你沒(méi)有想到的Global.asax怪事!

或許有些人會(huì)這樣寫他們的初始化代碼:

  1. void Application_Start(object sender, EventArgs e)  
  2. {  
  3.     //在應(yīng)用程序啟動(dòng)時(shí)運(yùn)行的代碼  
  4.     try {  
  5.         AppInitializer.Init();  
  6.     }  
  7.     catch( Exception ex ) {  
  8.         LogException(ex);  
  9.           
  10.         // .....................  
  11.     }  
  12. }  

這段代碼有什么問(wèn)題呢?

其實(shí)問(wèn)題的線索在于:為什么要加try....catch語(yǔ)句,是因?yàn)橹揽赡軙?huì)發(fā)生異常嗎?

如果真有異常情況發(fā)生,這樣處理后,后續(xù)的請(qǐng)求是不是會(huì)發(fā)生各種想像不到的錯(cuò)誤?

顯然這里不能吃掉異常,要不然后面的請(qǐng)求肯定會(huì)有問(wèn)題,因?yàn)樗鼈円蕾嚨脑O(shè)置沒(méi)有正確的初始化。

好吧,那我去掉 try.....catch語(yǔ)句,這樣總該行了吧:

  1. void Application_Start(object sender, EventArgs e)  
  2. {  
  3.     //在應(yīng)用程序啟動(dòng)時(shí)運(yùn)行的代碼  
  4.  
  5.     AppInitializer.Init();  
  6. }  

還是看來(lái)一下真實(shí)的運(yùn)行情況吧。

噢,抱歉,我還真忘記了配置連接字符串,這個(gè)異常提示太給力了。

現(xiàn)在就加上連接字符串嗎?

別急,想像一下,如果這個(gè)網(wǎng)站是一個(gè)真實(shí)的在線網(wǎng)站,會(huì)是什么情況呢?
答案有二種:

1. 另一個(gè)用戶也發(fā)起了一次請(qǐng)求。

2. 當(dāng)前用戶看到錯(cuò)誤頁(yè)面后,重新刷新了一次當(dāng)前頁(yè)面。

現(xiàn)在我用Opera來(lái)扮演第二個(gè)瀏覽用戶吧,還是打開(kāi)同樣的網(wǎng)址。

太奇怪了,第二個(gè)用戶居然能打開(kāi)頁(yè)面,好吧,讓他登錄試試。

結(jié)果第二個(gè)用戶看到的錯(cuò)誤情況和第一個(gè)用戶完全不同。

如果此時(shí)第一個(gè)用戶刷新他的瀏覽器,發(fā)現(xiàn)頁(yè)面又可以顯示了,然而登錄時(shí),會(huì)看到與第二個(gè)用戶一樣的異常信息。

這個(gè)示例代碼實(shí)在太簡(jiǎn)單了,我想維護(hù)人員根據(jù)NullReferenceException這個(gè)線索找下去,很快就能找到答案。如果初始化代碼再?gòu)?fù)雜一些,比如SetSqlDependency()中出現(xiàn)異常呢,那么程序仍然能夠正常運(yùn)行,但是我們期望的緩存依賴可能就沒(méi)有效果了,最終可能會(huì)產(chǎn)生性能問(wèn)題,排查的難度就會(huì)大多了。

記得以前做項(xiàng)目時(shí),就遇到過(guò)這種情況,當(dāng)時(shí)感到很奇怪,為什么刷新一下就沒(méi)黃頁(yè)了,不過(guò)后面的錯(cuò)誤就很折騰人了,最終也讓我總結(jié)了這個(gè)教訓(xùn)。所以我建議:如果在初始化階段出現(xiàn)了異常,干脆就別讓程序繼續(xù)運(yùn)行了,每個(gè)請(qǐng)求都直接顯示黃頁(yè),直到排除故障為止。

#p#

如何保證初始化異常一直顯示?

當(dāng)初始化發(fā)生異常時(shí),如何保證初始化異常一直顯示呢?

方法其實(shí)并不難,我們需要修改一下代碼:

  1. private static Exception s_initException;  
  2.  
  3. void Application_Start(object sender, EventArgs e)  
  4. {  
  5.     try {  
  6.         AppInitializer.Init();  
  7.     }  
  8.     catch( Exception ex ) {  
  9.         // 記下初始化的異常。  
  10.         s_initException = ex;  
  11.     }      
  12. }  
  13.  
  14. protected void Application_BeginRequest(object sender, EventArgs e)  
  15. {  
  16.     // 如果存在初始化異常,就拋出來(lái)。  
  17.     // 直到開(kāi)發(fā)人員發(fā)現(xiàn)這個(gè)異常,并已解決了異常為止。  
  18.     if( s_initException != null )  
  19.         throw s_initException;  
  20. }  

現(xiàn)在不管有多少個(gè)用戶來(lái)訪問(wèn),或者第一個(gè)訪問(wèn)者刷新瀏覽器多少次,都會(huì)看到同樣的異常信息:

說(shuō)明:Global.asax的這個(gè)問(wèn)題在IIS7以上版本的集成模式下并不存在。

還有哪些初始化方法?

除了Global.asax中的Application_Start,還有哪些方法可以在ASP.NET程序執(zhí)行初始化的任務(wù)呢?

目前我知道的還有另三種方法:

1. App_Code中的AppInitialize方法。

2. 寫個(gè)專用的HttpModule。

3. ASP.NET 4.0的PreApplicationStartMethodAttribute

App_Code中的AppInitialize方法

ASP.NET允許我們?cè)贏pp_Code中的任何一個(gè)類型定義一個(gè)AppInitialize方法,用它也能執(zhí)行初始化的任務(wù)。

  1. public class Class1  
  2. {  
  3.     public static void appInitialize()  
  4.     {  
  5.         AppInitializer.Init();          
  6.     }  
  7. }  

如果我此時(shí)再次運(yùn)行示例程序(已注釋掉Global.asax中的代碼),會(huì)看到以下顯示:

顯然,我們期望的初始化代碼確實(shí)被調(diào)用了。

這個(gè)AppInitialize方法有什么限制呢?

我們還是來(lái)看一下ASP.NET的源代碼吧:

  1. internal class BuildResultMainCodeAssembly : BuildResultCompiledAssembly  
  2. {  
  3.     private MethodInfo FindAppInitializeMethod(Type t)  
  4.     {  
  5.         return t.GetMethod("AppInitialize",   
  6.             BindingFlags.Public | BindingFlags.Static | BindingFlags.IgnoreCase,   
  7.             nullnew Type[0], null);  
  8.     }  

根據(jù)代碼我們可以發(fā)現(xiàn)AppInitialize方法的特點(diǎn)有:

1. 必須是一個(gè)公開(kāi)的靜態(tài)方法:BindingFlags.Public | BindingFlags.Static

2. 方法名不區(qū)分大小寫:BindingFlags.IgnoreCase

3. 方法不允許有傳入?yún)?shù):new Type[0]

HttpModule也能執(zhí)行初始化的任務(wù)

由于HttpModule總是會(huì)在ASP.NET管線中被調(diào)用,所以,我們還可以用它來(lái)完成初始化的操作。

  1. public class InitAppModule : IHttpModule  
  2. {  
  3.     public void Init(HttpApplication context)  
  4.     {  
  5.          //注意:Init事件可能被多次調(diào)用,所以這個(gè)方法會(huì)被多次調(diào)用。  
  6.  
  7.         AppInitializer.Init();  
  8.     }  

正如代碼注釋所說(shuō)的那樣,這種調(diào)用代碼是不對(duì)的,除非你能接受初始化代碼被多次調(diào)用!

所以,我們應(yīng)該按單例模式的思路來(lái)改寫代碼:

  1. private static readonly object s_lock = new object();  
  2. private static bool s_initOK;  
  3.  
  4. public void Init(HttpApplication context)  
  5. {  
  6.     lock( s_lock ) {  
  7.         if( s_initOK == false ) {  
  8.             //保證初始化代碼只執(zhí)行一次。  
  9.  
  10.             AppInitializer.Init();  
  11.             s_initOK = true;  
  12.         }  
  13.     }  
  14. }  

如果你希望代碼簡(jiǎn)單一點(diǎn),還可以這樣實(shí)現(xiàn):

  1. public class InitAppModule : IHttpModule  
  2. {  
  3.     static InitAppModule()  
  4.     {  
  5.         AppInitializer.Init();  
  6.     }  
  7.     public void Init(HttpApplication context)  
  8.     {  
  9.         // 留個(gè)空方法,  
  10.         // ASP.NET會(huì)調(diào)用這個(gè)方法,最后能觸發(fā)靜態(tài)方法的調(diào)用。  
  11.     }  

ASP.NET 4.0新增的初始化方法

為了讓一些類庫(kù)能自動(dòng)執(zhí)行一些初始化,ASP.NET提供了一種新方法,允許為程序集指定一個(gè)PreApplicationStartMethodAttribute

為了演示這種用法,我將前面的示例(VS2008開(kāi)發(fā))移到一個(gè)類庫(kù)中(用VS2012開(kāi)發(fā))并設(shè)置類庫(kù)的命名空間為InitClassLibrary1。

然后,我添加了一個(gè)調(diào)用類:

  1. namespace InitClassLibrary1  
  2. {  
  3.     public class Class1  
  4.     {  
  5.         public static void InitApp()  
  6.         {  
  7.             AppInitializer.Init();  
  8.         }  
  9.     }  

最后,我們可以在InitClassLibrary1類庫(kù)的AssemblyInfo.cs文件中,增加一個(gè)Attribute

  1. [assembly: System.Web.PreApplicationStartMethod(  
  2.                 typeof(InitClassLibrary1.Class1), "InitApp")]  

當(dāng)然了,你也可以直接像下面設(shè)置,免得多創(chuàng)建一個(gè)類型出來(lái):

  1. [assembly: System.Web.PreApplicationStartMethod(  
  2.                 typeof(InitClassLibrary1.AppInitializer), "Init")]  

這樣設(shè)置后,再運(yùn)行網(wǎng)站,你也能發(fā)現(xiàn)我們的初始化代碼確實(shí)運(yùn)行了:黃頁(yè)仍然在顯示。

#p#

各種初始化方法的差別

前面介紹了4種在ASP.NET執(zhí)行初始化的方法,你或許想知道它們到底有哪些區(qū)別呢?

由于它們都能實(shí)現(xiàn)初始化的操作,它們的差別也只有執(zhí)行的時(shí)刻不同而已,我們可以用簡(jiǎn)單的方法區(qū)分它們的調(diào)用位置:看異常的調(diào)用堆棧信息。

AppInitialize方法異常時(shí)的調(diào)用堆棧信息:

HttpModule異常時(shí)的調(diào)用堆棧信息:

PreApplicationStartMethodAttribute異常時(shí)的調(diào)用堆棧信息:

Global.asax的Application_Start事件處理器的調(diào)用方式則不同,ASP.NET采用了反射調(diào)用,當(dāng)異常發(fā)生只保留了內(nèi)部異常,我們看不到調(diào)用堆棧(不信的話,自己去試試)。

沒(méi)關(guān)系,既然ASP.NET不告訴我們調(diào)用堆棧信息,我們自己也可以去取,請(qǐng)看下面的代碼:

  1. void Application_Start()  
  2. {  
  3.     System.Diagnostics.StackTrace stack = new System.Diagnostics.StackTrace();  
  4.     System.IO.File.WriteAllText("h:\\Application_Start_stack.txt", stack.ToString());  
  5. }  

再打開(kāi)文件看一下吧。

說(shuō)明:Global.asax的Application_Start事件處理器還有幾種等效的方法:

  1. // 這二個(gè)方法都可以實(shí)現(xiàn)與Application_Start(object sender, EventArgs e)相同的行為。  
  2.  
  3. void Application_OnStart()  
  4. {  
  5. }  
  6.  
  7. void Application_Start()  
  8. {  
  9. }  

根據(jù)以上分析,可以可以得知:

1. AppInitialize和PreApplicationStartMethodAttribute指向的方法被調(diào)用的時(shí)機(jī)發(fā)生在ASP.NET創(chuàng)建宿主環(huán)境時(shí),屬于比較早的時(shí)刻。

2. Application_Start和HttpModule的調(diào)用時(shí)刻要晚一點(diǎn)。

這個(gè)結(jié)論有用嗎?

其實(shí)我也感覺(jué)意義不大,不過(guò)分析它僅僅為了滿足我的求知欲和好奇心而已,你是否也有這樣的好奇心呢?

如果你仍然好奇想知道這4種方法的執(zhí)行時(shí)機(jī)的先后順序,我也能告訴你:

1. PreApplicationStartMethodAttribute指向的方法。

2. App_Code中的appInitialize方法。

3. Application_Start。

4. HttpModule

再補(bǔ)充一點(diǎn):在開(kāi)發(fā)環(huán)境中,當(dāng)我們編譯網(wǎng)站時(shí),PreApplicationStartMethodAttribute指向的方法可能會(huì)被調(diào)用,這處決于類庫(kù)的程序集是否發(fā)生了修改。

到底該選擇哪種初始化方法?

今天給大家介紹了4種在ASP.NET中執(zhí)行初始化的方法,或許有些人會(huì)想:到底該選擇哪種初始化方法呢?

的確,方法越多越讓人迷惑。

下面的觀點(diǎn)僅代表我個(gè)人的建議,你也可以根據(jù)自己的喜好來(lái)選擇。

1. 優(yōu)先選擇Application_Start(雖然IIS的經(jīng)典模式下需要多寫點(diǎn)代碼),因?yàn)槿魏稳苏页跏蓟a時(shí)都會(huì)想到那里,便于其他人維護(hù)。

2. AppInitialize方法雖然使用簡(jiǎn)單,但它并不適合于WebApplication項(xiàng)目。

3. PreApplicationStartMethodAttribute只支持ASP.NET 4.0以上版本,且尤其適合于類庫(kù)的內(nèi)部初始化。

4. 當(dāng)以上方法都不可行時(shí),HttpModule將成為最后的救命稻草,它適合所有ASP.NET版本。

原文鏈接:http://www.cnblogs.com/fish-li/archive/2013/03/24/2979780.html

責(zé)任編輯:張偉 來(lái)源: 博客園
相關(guān)推薦

2009-03-30 10:34:03

ASP.NETMySQL

2009-07-29 10:30:53

Web應(yīng)用程序ASP.NET

2017-04-21 12:03:46

MacASP.NET Cor程序

2009-07-20 16:08:04

ASP.NET應(yīng)用程序

2009-07-27 16:09:50

2009-07-29 17:01:13

2009-08-05 10:16:54

部署ASP.NET應(yīng)用

2009-07-22 17:32:40

ASP.NET應(yīng)用程序

2009-07-23 13:26:21

2009-07-21 15:02:19

ASP.NET應(yīng)用程序

2009-07-23 14:25:03

ASP.NET 2.0

2009-07-29 17:21:10

2009-07-21 15:23:55

預(yù)編譯Web應(yīng)用程序ASP.NET

2009-07-28 10:11:06

ASP.NET應(yīng)用程序

2009-07-24 10:41:00

ASP.NET Web

2009-08-04 11:46:09

2009-07-21 15:14:32

預(yù)編譯應(yīng)用程序ASP.NET

2009-07-22 18:07:55

論壇應(yīng)用程序ASP.NET MVC

2009-07-24 11:25:53

Web應(yīng)用程序工程ASP.NET MVC

2009-07-27 17:46:42

WCF服務(wù)ASP.NET應(yīng)用程序
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)