關於C# XML序列化的一個BUG的修改


關於C# XML序列化的一個BUG的修改

在我前一篇博客中提到用XML序列化作為數據庫的一個方案,@拿筆小心 提到他們在用XML序列化時,遇到了一個比較嚴重的bug,即XML不閉合,系統不能正確的加載此XML。在我的開發經驗中,也遇到過這樣的問題。現在把這個BUG的描述及解決方案記錄如下,也供遇到此BUG的朋友參考。

BUG描述

這個BUG的出現也是比較詭異的,我們給客戶做的一套系統,這個系統會把數據寫到N個xml文件中,正常情況下都沒有問題。直到有一天……客戶運行程序運行了一天,到快下班的時候,把數據保存到數據庫中;第二天來上班時,忽然發現數據都沒有了,也就是說昨天一天的工作白做了。

當客戶把這個BUG告訴我的時候,我第一時間的反應是要重現這個BUG。因為同樣的系統N份已經運行了一年了,從來沒有出現過這個問題。結果客戶在同樣的機器上再次測試,沒有遇到這個問題。我以為這個BUG是偶然現象,就沒有處理,結果噩夢開始了。

當客戶把系統部署到生產系統中之后,生產系統中偶爾也出現這個問題,每次出現這個問題,基本上耽誤了一天的工作,損失都是N萬,當時壓力巨大,趕緊扎到現場解決問題。

我發現之所以以前沒有出現這個BUG,是因為以前的數據量都非常少,但這個版本的數據量很大。我觀察了數據文件,發現是XML文件丟失了一部分結尾造成的。如丟失一個>號,導致不能正確加載XML,數據丟失。

解決方案1

既然定位到了問題,那就有解決方案了。每次寫完數據之后,我都會重新LOAD一下數據以驗證正確性,如果不正確,則重新寫入數據。用這個思路,我很快改了一版,部署到生產環境中。(測試環境很難重新這個錯誤)。

然而,問題還是沒有解決,一個月之后,同樣的問題又出現了。看來必須找到根本原因,不能取巧解決問題。

解決方案2

我開始google這個問題,最后在stackoverflow上找到:xdocument save adding extra characters。他的描述是XML會增加字符,我的問題是會減少字符。

原來XML的寫入方式是:

config.Save(new FileStream(@"c:\foo.xml", FileMode.Create, FileAccess.Write), SaveOptions.None);

應該改為

using (FileStream fs = new FileStream(@"C:\foo.xml", FileMode.Truncate, FileAccess.Read))
{
    config.Load(fs);
}

主要修改是把FileMode.Create改為FileMode.Truncate。

Use FileMode.Truncate in your write FileStream so that the file is truncated to 0 bytes before you start writing to it.

這個方案看起來是靠譜的,然而,我還是不放心。

解決方案3

為了防止再次出問題,我寫了一個FixErrorXmlFile方法,解決去除xml多字符的問題,在每次加載xml的時候,如果出現錯誤,調用此方法修正xml錯誤。其核心代碼如下:

  private static bool ReadFile(string filePath, out string realContent)
        {
            string content = string.Empty;
            realContent = string.Empty;
            using (FileStream fs = new FileStream(filePath, FileMode.Truncate))
            {
                using (StreamReader sr = new StreamReader(fs))
                {
                    content = sr.ReadToEnd();
                }
            }
            //首先,要找到文件頭末尾的'>'(即第一個右尖括號)的索引值index1,如果index1的值小於1,說明'>'不存在,跳出:否則往下執行
            //然后,找到根元素左側的'<'的索引值index2,同樣,如果'<'存在繼續往下執行
            //      找到根元素右側的第一個'>'的索引值index3和第一個' '的索引值index4
            //      比較index3和index4,較小者為根元素右側的第一個元素的索引
            //      找出根元素的名稱
            //接着,找到最后一個匹配根元素名稱的開始位置index5
            //最后,確定根元素右側第一個'>'的索引值,來獲取文件的真正內容realContent
            int index1 = content.IndexOf('>');
            if (index1 < 1)
            {
                return false;
            }
            int index2 = content.IndexOf("<", index1);
            if (index2 < 1)
            {
                return false;
            }
            int index3 = content.IndexOf(">", index2);
            int index4 = content.IndexOf(" ", index2);
            int index = index3 < index4 ? index3 : index4;
            string rootName = content.Substring(index2 + 1, index - index2 - 1);
            int index5 = content.LastIndexOf(rootName);
            if (index5 < 1)
            {
                return false;
            }
            index5 += rootName.Length;
            realContent = content.Substring(0, index5 + 1);
            return true;
        }

后記

我同時把解決方案2和解決方案3都修改了,再次放到了生產系統中。一年過去了,再也沒有出現過這個BUG。這個一年指的是同時5台機器一直不停的運行。

后來我又觀察了一下,好像我的解決方案3根本就沒起作用,從來沒有進入過這個函數。也就是說,解決方案2已經解決了這個問題。

雖然這個問題沒有重現,但我還是不認為這個問題已經完美解決。我認為這是微軟的一個BUG,在正常應用序列化的情況下會出現丟失數據,應該由微軟來解決,而不是采用其它的補丁方式解決問題。微軟類似的BUG遇到好幾個了。

總之,這個問題就是這樣解決了,希望對遇到相似問題的人有所幫助。也歡迎大家指出我的問題。

參考:

http://www.cnblogs.com/wardensky/p/4170605.html

我同事當時記錄的這個問題:xml存儲bug


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM