程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> XmlSerializer的坑

XmlSerializer的坑

編輯:C#入門知識

XmlSerializer我想現在用的人可能不多了,大家都在用Json。我現在所在的公司依然在用,所以發現了這個坑。當然這個坑存在很久了只是沒用過所以才發現。 事情是這樣的,測試那邊說系統偶爾會報找不到 xxxx.XmlSerizlizers 的引用,File Not Found的異常,幾率不高。但是我百般尋找發現項目了根本就沒有這個dll,為什麼會找這個dll呢? 後來經過各種查找原因,發現是項目引用了公司的一個框架,這個框架記錄了此異常,他是如何記錄的呢?   internal static void OnFirstChanceException(object sender, FirstChanceExceptionEventArgs e)   {     if (HttpContext.Current == null || e.Exception == null || e.Exception.Source == "Arch.CFramework.StartUp")       return;     if (HttpContext.Current.Items[(object) "__RequestFirstChangeExceptionKey__"] == null)       HttpContext.Current.Items[(object) "__RequestFirstChangeExceptionKey__"] = (object) new RequestException();     (HttpContext.Current.Items[(object) "__RequestFirstChangeExceptionKey__"] as RequestException).Exceptions.Add(e.Exception);   }     它記錄了FirstChangeException,只要有FirstChangeException,他就會記錄下來,看來XmlSerializer會有這個異常。於是在new XmlSerializer的時候F10,跟蹤進去發現確實觸發了FileNotFound的異常,找Assembly.Load找不到xxxx.XmlSerializers引用。這台奇怪了,就new下XmlSerializer為什麼會找不相干的DLL? 搜索google找到了這篇問答:http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor 這裡面說的比較清楚, Like Martin Sherburn said, this is normal behavior. The constructor of the XmlSerializer first tries to find an assembly named [YourAssembly].XmlSerializers.dll which should contain the generated class for serialization of your type. Since such a DLL has not been generated yet (they are not by default), a FileNotFoundException is thrown. When that happenes, XmlSerializer's constructor catches that exception, and the DLL is generated automatically at runtime by the XmlSerializer's constructor (this is done by generating C# source files in the %temp% directory of your computer, then compiling them using the C# compiler). Additional constructions of an XmlSerializer for the same type will just use the already generated DLL. .net的實現機制是先去找[YourAssembly].XmlSerializers.dll,找不到就會拋出FileNotFoundExcpetion,然後XmlSerializer的構造函數捕獲到這個異常之後,就會動態生成這個dll放在%temp%下,然後再用它。我勒個擦,這不是很坑爹的機制嗎?到了4.5版本,就不在這麼實現了。 Starting from .NET 4.5, XmlSerializer no longer performs code generation nor does it perform compilation with the C# compiler in order to create a serializer assembly at runtime, unless explicitly forced to by setting a configuration file setting (useLegacySerializerGeneration). This change removes the dependency on csc.exe and improves startup performance. Source: .NET Framework 4.5 Readme, section 1.3.8.1. 現在的問題是,這個異常一般是不會有人知道的,除非捕獲FirstChangeException。 雖然這個異常只會發生一次,但是如果應用程序池回收了(XmlSerializer會緩存Assembly),%temp%沒有了,就會重新生成,還是會有一點點的影響,總是讓人不舒服。 所幸,帖子中有提到一個方法XmlSerializer.FromTypes,這個不會觸發異常,但他不會利用緩存,說是會內存洩露,如下: WARNING: You will leak memory like crazy if you use this method to create instances of XmlSerializer for the same type more than once! This is because this method bypasses the built-in caching provided the XmlSerializer(type) and XmlSerializer(type, defaultNameSpace) constructors (all other constructors also bypass the cache). If you use any method to create an XmlSerializer that is not via these two constructors, you must implement your own caching or you'll hemorrhage memory. – Allon Guralnek 這個實驗,我就不做了,不會緩存沒問題,我們自己緩存便是。所以我寫了一個測試程序, class Program   {       private static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();          private static XmlSerializer GetSerializer<T>()       {           var type = typeof(T);           if (_cache.ContainsKey(type))           {               return _cache[type];           }              var serializer = XmlSerializer.FromTypes(new[] { typeof(Test) }).FirstOrDefault();           _cache.Add(type, serializer);              return serializer;       }          private static void Serializer<T>(XmlSerializer xmlSerializer,T ob)       {           MemoryStream memoryStream = new MemoryStream();           xmlSerializer.Serialize((Stream)memoryStream, ob);           var str = Encoding.UTF8.GetString(memoryStream.GetBuffer());           memoryStream.Close();           memoryStream.Dispose();       }          static void Main(string[] args)       {           var ns = typeof(Test).Namespace;           var maxtimes = 1000*1000;           var t = new Test { Name = "test" };           var sw = new Stopwatch();           sw.Start();           for (var i = 0; i < maxtimes; i++)           {               var s = GetSerializer<Test>();               //Serializer(s, t);           }           sw.Stop();              Console.WriteLine("FromType:" + sw.ElapsedMilliseconds + " ms");              sw = new Stopwatch();           sw.Start();           for (var i = 0; i < maxtimes; i++)           {               var s = new XmlSerializer(typeof(Test));               //Serializer(s, t);           }           sw.Stop();           Console.WriteLine("New:" + sw.ElapsedMilliseconds + " ms");       }   }     緩存的只用100ms,直接new的用了6000ms。顯然用FromTypes最好,不但解決了異常問題,還提升了效率。 於是順手寫了一個XmlSerializerHelper類,供大家參考使用: public static class XmlSerializerHelper   {          private static ConcurrentDictionary<Type, XmlSerializer> _cache;       private static XmlSerializerNamespaces _defaultNamespace;          static XmlSerializerHelper()       {           _defaultNamespace = new XmlSerializerNamespaces();           _defaultNamespace.Add(string.Empty, string.Empty);              _cache = new ConcurrentDictionary<Type, XmlSerializer>();       }             private static XmlSerializer GetSerializer<T>()       {           var type = typeof(T);           return _cache.GetOrAdd(type, XmlSerializer.FromTypes(new[] { type }).FirstOrDefault());       }             public static string XmlSerialize<T>(this T obj)       {           using (var memoryStream = new MemoryStream())           {               GetSerializer<T>().Serialize(memoryStream, obj, _defaultNamespace);               return Encoding.UTF8.GetString(memoryStream.GetBuffer());           }       }          public static T XmlDeserialize<T>(this string xml)       {           using (var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(xml)))           {               var obj = GetSerializer<T>().Deserialize(memoryStream);               return obj == null ? default(T) : (T)obj;           }       }   }       【PS】假如你的項目有用到new XmlSerializer,那麼想看到異常很簡單。 1、Tools—>Options—>Debugging—>Enable Just My Code勾去掉 2、Debug—>Exceptions—>Common Language Runtime Exceptions 找到System.IO.FileNotFoundException,throw勾打上 ,再F5調試看看。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved