﻿# 在寫 ASP.NET 的時候要謹慎使用靜態(static)欄位

最近發現了一個之前開發過的網站，大約兩、三天就會發生「已經開啟一個與這個 Command 相關的 DataReader，必須先將它關閉。」的錯誤訊息，整個網站可以正常編譯，代表語法沒問題，測試的時候可以正常執行也不會發生 Exception，但只要一到了客戶那邊的主機執行個幾天就會開始出現問題，但通常多 reload 幾次網頁就會正常執行就好了，完全是一個很詭異的狀況。

程式碼很簡單，如下：

```
public class Counter
{
    protected static ExtensionDataSetTableAdapters.ClickBlogCounterTableAdapter 
        ClickBlogCounterTA = new ExtensionDataSetTableAdapters.ClickBlogCounterTableAdapter();

    public static int getClickBlogCount()
    {
        return (int)ClickBlogCounterTA.getCount();
    }
}
```

錯誤出現在 "**return (int)ClickBlogCounterTA.getCount();**" 這一行，其中 **ClickBlogCounterTA** 是在 Typed DataSet 中的一個 TableAdapter 物件，而這個靜態方法是在 App\_Code 底下的一個類別中。

完整的錯誤訊息如下：

```
System.Web.HttpUnhandledException: 已發生類型 'System.Web.HttpUnhandledException' 的例外狀況。 ---> System.InvalidOperationException: 已經開啟一個與這個 Command 相關的 DataReader，必須先將它關閉。
   於 System.Data.SqlClient.SqlInternalConnectionTds.ValidateConnectionForExecute(SqlCommand command)
   於 System.Data.SqlClient.SqlConnection.ValidateConnectionForExecute(String method, SqlCommand command)
   於 System.Data.SqlClient.SqlCommand.ValidateCommand(String method, Boolean async)
   於 System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(DbAsyncResult result, String methodName, Boolean sendToPipe)
   於 System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
   於 ExtensionDataSetTableAdapters.ClickBlogCounterTableAdapter.getCount() 於 c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\root\bcc246a4\3746b4ea\App_Code.eopvwaqp.10.cs: 行 8729
   於 Counter.getClickBlogCount() 於 c:\AAA\BBB\App_Code\Counter.cs: 行 62
   ......
```

這段 Code 不是我寫的，加上最近有點忙，所以問題一直擱著，但我找了個幾個朋友讓他們看 Code 請他們找出問題所在，但他們也都覺得這段 Code 沒問題，大家也提出一些「想法」與「解決方案」，大多人提供兩點建議：

1.  **加上 try / catch 就好啦**：基本上，這是最爛的答案了，這等於是逃避問題嘛！不過這也是最懶、最快、最省事、也最輕鬆的解決方案了，但這不是我要的選項。
2.  **這一看就知道是微軟的問題嘛，錯誤是在 Typed DataSet 自動產生的程式碼中出錯的啊**：ㄟ...，我也曾經一度懷疑啦，不過就以往的經驗來說，通常都是自己寫的程式錯誤的機率比較高，這應該是排在最後的一個選項了，若真的是這樣，就只好用第一點的建議了。^\_^

就因為錯誤不容易重現，程式碼又可以正常執行，我昨天花了些時間仔細推敲程式碼的結構，最後終於看出問題所在，並將這個問題徹底解決了。

首先有個很重要的觀念需要說明，若你在 ASP.NET 中使用靜態欄位(static field)的話，這個靜態欄位物件會一直留存在 ASP.NET 的應用程式中 (HttpApplication)，也就是說當 ASP.NET 頁面處理完成之後，該物件還是一樣存在 HttpApplication 中，並且所有 HTTP Request 都會共用這個靜態欄位變數，所以只要網站流量變多的時候就很有可能會發生資源衝突的情況，導致 TableAdapter 在進行資料處理的時候因為只共用一條連線，而造成連線狀態彼此衝突而混亂的情況。

我個人在寫 ASP.NET 就是因為知道會有這種狀況，所以本身就很少用 static 宣告變數，除非你真的很清楚使用 static 變數的時機與潛在會發生的問題，就可以大膽去用啦。

講到這裡，讓我想到一個好玩的排行榜，就是當程式不會動或有問題的時候，通常程式設計師的回答如下：

-   第 20 名：這很奇怪喔。
-   第 19 名：以前從來不會這樣啊！
-   第 18 名：昨天明明會動的啊！
-   第 17 名：怎麼可能～
-   第 16 名：這一定是機器的問題。
-   第 15 名：你到底是打了什麼才讓程式當掉的？
-   第 14 名：一定是你的資料有問題。
-   第 13 名：我已經好幾個禮拜沒碰那一段程式了。
-   第 12 名：你一定是用到舊版了。
-   第 11 名：一定是巧合！為什麼這種壞運氣只讓你碰上。
-   第 10 名：我不可能什麼功能都測試到吧，有 bug 是正常的！
-   第 9 名：這個不可能是那個的原始碼！
-   第 8 名：這程式應該是會動的，只是我寫好後還沒做測試。
-   第 7 名：可惡！一定有人改了我的程式。
-   第 6 名：你有檢查過你的電腦有沒有病毒嗎？
-   第 5 名：儘管這功能還不能動啦，你覺得他如何？
-   第 4 名：在你的系統不能用那一個版本的程式啦！
-   第 3 名：你幹嘛要那樣操作，都是你的問題。
-   第 2 名：程式發生問題時你在哪裡？
-   第 1 名：在我的機器明明就可以動啊！
-   **萬用答案**：電腦請重開，應該就會好了!

另外，工程師常說的話還有以下幾項，你也可以看看你常說哪幾項：

-   25.都這樣了，還不work，搞什麼？
-   24.你可能中毒了喔。
-   23.一定是有人改了我的程式。
-   22.已經可以了，不過還沒測試過喔。
-   21.都好了啊，還沒測試過就是了。
-   20.我不是已經修好了嗎？
-   19.這個不能那個。(THIS can't do THAT.)
-   18.我一個人又測不完！
-   17.怎麼這麼衰！
-   16.沒問題，馬上好！
-   15.當然，當然，我再修一修就可以了。
-   14.快好了，快好了。
-   13.好啦，只不過一個小功能嘛！
-   12.你拿錯執行檔了。
-   11.可以，可以，來得及。
-   10.我可沒動過這個模組喔！
-   9.你的測試資料一定有錯！（我那邊不會啊！）
-   8.不可以這樣操作的啦！
-   7.你的作業系統（驅動程式）升級了沒有啊？
-   6.機器好像壞了。
-   5.怎麼可能？！
-   4.哦，這程式還要改一下。
-   3.昨天還好好的呀！
-   2.我從來不知道有這種事。
-   1.奇怪...
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   最後，也是最常用的
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   .
-   0.更
