從.NET到Mono-記Kooboo CMS對Mono的兼容歷程:三、平臺的兼容性

從基於.NET Framework遷移到Mono平臺,除了經常會遇到大小寫敏感的問題之外,還會經常遇到一些平臺不兼容性的問題,這也是我的經歷當中,遇到的另一個比較麻煩的問題。其實關於Mono平臺的兼容性,在官方的文檔中已經釋放了比較明確的信息,他們就明確的一句話來描述Mono的兼容性:“Everything in .NET 4.0 except WPF, EntityFramework and WF, limited WCF.”。但是實際上並不是這麼簡單的!在Kooboo CMS中,目前沒有用過WPF、EF、WF、WCF,但是仍然遇到了幾個由於平臺的兼容性而導致的問題。這些不兼容,本身就是Mono設計的意圖,出於操作系統平臺的差異性,Mono可能會選擇對部分功能的不兼容;我還遇到了一些在.NET Framework中編譯正常的代碼,但是在Mono中無法編譯通過,這種可能是對編譯器嚴謹性設計上的不同;同時,對於已支持的API,雖然功能確實是支持,但是我們還是會得到一些與.NET Framework不大一樣的結果而造成不兼容的情況出現。

對於程序集出現不兼容的情況時,不像.NET Framework在啓動時經常出現的各種程序集無法載入而讓整個站點無法啓動,MONO的策略是比較好的。我們完全可以把在.NET中編譯好的Web站點,直接放到Mono運行時去執行。這時候,假定你程序中使用了MONO不支持的某些程序集或API的時候,它並不會馬上就告訴你程序無法啓動,而是隻要你還沒有用到這些功能,程序就還可以正常運行。所以我們會經常發現,我們發佈好的站點,可能可以直接在MONO中跑起來,但是當我們把源碼用MonoDevelop打開時,卻無法編譯通過,出現不少的編譯錯誤。我很喜歡這點設計。

儘管如此,要讓ASP.NET站點可以完全兼容MONO,調試是必不可少的。所以我們必須保證我們的源碼可以在MonoDevelop中正常打開,並且完全編譯通過。下面的幾點不兼容或是在編譯時就發現,或是程序出錯了錯誤後,通過MonoDevelop的調試發現並且解決。讓我們一起來看一下吧:

  1. ASP.NET HealthMonitoring的不兼容。在源碼用MonoDevelop打開之後,一編譯就發現好多好多的錯誤,其中大部分的錯誤是出現在所重寫的幾個HealthMonitoring的類上面。經常一段時間的研究發現,Mono在System.Web.Management的空間下,只有少數的幾個類,而且這些類的大部分都沒有具體的實現。對於這點不兼容,自然沒什麼好說的,也沒有好的解決辦法,再找一個好的替代方案。不過,我相信這個功能應該是可以實現的,除了Eventlog有系統平臺特性之外,應該沒有更多的平臺依賴。
  2. 在編譯錯誤中,有一行代碼,在.NET中運行正常,在MONO中卻出錯,提示我們需要做一下類型轉換。爲了更好的說明這個編譯錯誤,我把代碼原型提煉出來:
    public interface IReadonly
    {
        string Name{get;}
    }
    public class BaseClass
    {
        public string Name {
            get;
            set;
        }
    }
    public class Class1:BaseClass,IReadonly
    {
    }
    class MainClass
    {    
        public static void SetName<T>(T o)
            where T:BaseClass,IReadonly
        {        
            o.Name= "";
        }
    }
    

    這段代碼中,o.Name=“”這行在Mono中會提示我們“Ambiguity between BaseClass.Name and IReadonly.Name”。對於這個問題,解決方案很簡單,只需要做對o作一下類型轉換,轉成BaseClass的類型再賦值就可以通過了。我本身對編譯器的行爲了解不是特別多,所以講不出個所以然出來。只能說,這是兩種編譯器在類型處理嚴謹性上面的不同吧。

  3. 對於Mono支持的API,我們仍然也需要經過運行測試之後才能保證它是否工作正常。相同的一個API,不同的平臺,產生不同的結果,這個是很容易理解的。我這邊就遇到兩個這樣的問題。
    • 在使用KeyedHashAlgorithm對字符串進去HASH的結果不同。因爲我們使用了KeyedHashAlgorithm對用戶密碼進行一次加密,但是相同的用戶數據和輸入密碼在MONO運行時,總是提示密碼不正確。經過調試發現,是由於.NET和MONO所使用的密鑰長度不同,還好只是使用了密鑰長度。我們只需要讓它們使用一樣的長度就可以了,加上這行代碼:algorithm.Key = new byte[64];
    • 我使用了DataContractSerializer來序列化對象。在序列化普通對象的時候,它們的格式都是一致的。但是在序列化Dictionary對象的時候,.NET和Mono就會有一些差異,這些差異主要集中在XML名稱空間上。在.NET中,序列化的根結點的名稱空間是http://schemas.microsoft.com/2003/10/Serialization/Arrays,而MONO序列化後的根結點名稱空間爲:http://schemas.datacontract.org/2004/07/System.Collections.Generic。這就造成了平臺之間的數據切換的不兼容。不過,經過一段時間的研究還是找到的解決辦法,只需要在實例化DataContractSerializer的時候指定使用.NET的根結點名稱空間就可以解決問題:
      DataContractSerializer ser = new DataContractSerializer(typeof(List<Dictionary<string,object>>),"ArrayOfArrayOfKeyValueOfstringanyType","http://schemas.microsoft.com/2003/10/Serialization/Arrays",new Type[]{typeof(Dictionary<string,object>)})
      

      對於序列化的不兼容,這邊還有另一個問題,就是對IDictionary的處理手法上Mono和.NET還是有一些不同的。在.NET中,對象的屬性中包含IDictionary對象是可以正常序列化和反序列化,但是在MONO運行時就會提示“'this' type cannot be an interface itself”。解決這個錯誤的辦法就是把IDictionary改成爲Dictionary,用具體的類型,而不是用接口。

以上的這些問題都是我目前能記起來的所遇到的平臺的不兼容問題。大家可以看到,除了MONO本身不支持的功能和API之外,其它的兼容性問題經過一段時間的調試和研究之後都還是可以順利解決的。由此我相信,要讓一個普通的ASP.NET站點兼容MONO,應該不僅僅只存在於理論上的可能了。

本來計劃,本篇是作爲一個結束篇來寫的。但是在寫的過程中發現,還有一些其它方面的心得還漏掉很多,比如使用MonoDevelop的經驗,調試ASP.NET MVC程序的經驗等等。這些都與Windows/.NET平臺還是有一些區別的。所以下篇準備分享和記錄一下這方面的經驗。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章