4

ASP.NET 實戰手札:重啟網站才能解決的 TypeInitializationException 錯誤

 2 years ago
source link: https://blog.darkthread.net/blog/iis-typeinitializationexception-issue/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
ASP.NET 實戰手札:重啟網站才能解決的 TypeInitializationException 錯誤-黑暗執行緒

這是開發 ASP.NET 網站可能遇到的問題:當類別的靜態建構式、靜態欄位初始化出錯,將導致 TypeInitializationException 「'XXXX' 的類型初始設定式發生例外狀況。 The type initializer for 'XXXX' threw an exception.」錯誤。一旦發生,即使修正錯誤來源,靜態建構式、靜態屬性也不會重新執行,呼叫相關方法會得到相同例外(可想像成一直傳回被 Cache 的 TypeInitializationException),有時會讓人混淆,例如:缺少的檔案、Registry 明明已經補上,為什麼系統還是一直抱怨找不到?

用一個範例來重現問題。我在 IIS 開了一個 ASP.NET Web Site 應用程式 - MyWebSite,App_Code/AppUtil.cs 的靜態建構式會由 ~/secret.txt 讀取字串,之後透過 public static string GetSecret() 方法供外界讀取。(註:為便於觀察,讀檔動作加了 try catch 在錯誤訊息加註時間戳記。)

using System;
using System.Web.Hosting;
using System.IO;

public class AppUtil
{
    static string _secret;
    static AppUtil()
    {
        try 
        {
            _secret = File.ReadAllText(
                HostingEnvironment.MapPath("~/secret.txt"));
        }
        catch (Exception ex) 
        {
            throw new ApplicationException(ex.Message + "/" + 
                DateTime.Now.ToString("HH:mm:ss.fff"));
        }
    }

    public static string GetSecret()
    {
        return _secret;
    }
}

測試網頁 default.aspx 如下:

<%@ Page Language="C#" %>
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        Response.ContentType = "text/plain";
        try 
        {
            var secret = AppUtil.GetSecret();
            Response.Write("Secret = " + secret);
        }
        catch (Exception ex)
        {
            Response.Write(ex.GetType().ToString() + ":\n");
            Response.Write(ex.Message + "\n");
            if (ex.InnerException != null) 
            {
                Response.Write(ex.InnerException.Message + "\n");
            }
            Response.Write(DateTime.Now.ToString("HH:mm:ss.fff") + "\n");
        }
    }
</script>

開始測試時 secret.txt 不存在,呼叫 default.aspx 不意外地得到錯誤:

System.TypeInitializationException:
'AppUtil' 的類型初始設定式發生例外狀況。
找不到檔案 'C:\WWW\MyWebSite\secret.txt'。
HH:mm:ss.fff

接著我們現場建立 secret.txt,再執行一次 default.aspx,發現它仍然在抱怨找不到 secret.txt,但檔案明明在呀!

重啟 IIS AppPool 或 IISRESET 後,才會順利讀到 secret.txt 內容。

來個一鏡到底展示:

  1. 原本沒有 secret.txt
  2. default.aspx 如預期發生 TypeInitializationException,根本原因是靜態建構式找不到 secret.txt
  3. 寫入 secret.txt,確認檔案已存在
  4. 再次執行 default.aspx,仍在抱怨找不到 secret.txt,但由時間標籤可發現 TypeInitializationException 訊息與前次相同,但 default.aspx 加註的時間是新的
  5. 回收 AppPool
  6. 再執行 default.aspx,這才成功讀到 secret.txt

【結論】由實驗結果可知,ASP.NET 網站若因型別因靜態建構式、靜態欄位初始化出錯導致 TypeInitializationException 錯誤,即使排除錯誤根源仍會得到同一例外,直到重啟 AppPool 或 IISRESET 才能修正。明白這點,就不會被「明明已經調整過,卻一直彈出一模一樣錯誤訊息」所迷惑囉。
(不過,我找不到能解釋 TypeInitializationException 類似被 Cache 住現象的技術文件,歡迎大家補充。)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK