XmlSerializer ให้ FileNotFoundException ที่ตัวสร้าง


347

แอปพลิเคชันที่ฉันทำงานด้วยล้มเหลวเมื่อฉันพยายามเรียงลำดับประเภท

คำสั่งเช่น

XmlSerializer lizer = new XmlSerializer(typeof(MyType));

ผลิต:

System.IO.FileNotFoundException occurred
  Message="Could not load file or assembly '[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified."
  Source="mscorlib"
  FileName="[Containing Assembly of MyType].XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  FusionLog=""
  StackTrace:
       at System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)
       at System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection)

ฉันไม่ได้กำหนดเลขลำดับพิเศษสำหรับชั้นเรียนของฉัน

ฉันจะแก้ไขปัญหานี้ได้อย่างไร


5
ตกลงดังนั้นคำถามนี้เป็นเพียงคำถาม VB รุ่น C # ของฉันที่ถามไปแล้ว: stackoverflow.com/questions/294659/… ขอบคุณ
Irwin

1
เมื่อหกปีที่แล้วคำตอบของ @VladV เป็นทางออกที่ง่ายที่สุดและน้อยที่สุด เพียงเปลี่ยนรายการGenerate serialization assemblyแบบเลื่อนลงเป็น "เปิด" แทน "อัตโนมัติ"
Heliac

@Heliac: ฉันไม่เห็นด้วย มันไม่ได้ทำงานเสมอ โปรดดูความคิดเห็นของ Benoit Blanchon ต่อคำตอบของ Vlad คำตอบที่ง่ายที่สุดสำหรับฉันคือไม่ใช้ String.Collection ในไฟล์ปรับแต่ง ฉันใช้: string [] items = Settings.Default.StringofNewlineDelimitedItems.Split (ใหม่ [] {Environment.NewLine});
Andrew Dennison

คำตอบ:


388

เชื่อหรือไม่ว่านี่เป็นพฤติกรรมปกติ มีข้อผิดพลาดเกิดขึ้น แต่จัดการโดย XmlSerializer ดังนั้นหากคุณไม่สนใจก็ควรดำเนินการต่อไป

ฉันพบว่ามันน่ารำคาญมากและมีการร้องเรียนมากมายเกี่ยวกับเรื่องนี้ถ้าคุณค้นหารอบ ๆ แต่จากสิ่งที่ฉันอ่าน Microsoft ไม่ได้วางแผนที่จะทำอะไรเกี่ยวกับเรื่องนี้

คุณสามารถหลีกเลี่ยงการยกเว้นป๊อปอัปได้ตลอดเวลาในขณะที่การดีบั๊กถ้าคุณปิดข้อยกเว้นโอกาสแรกสำหรับข้อยกเว้นเฉพาะนั้น ใน Visual Studio ไปDebug -> ข้อยกเว้น (หรือกดCtrl+ Alt+ E) รันไทม์ภาษาทั่วไปยกเว้น -> System.IO -> System.IO.FileNotFoundException

คุณสามารถค้นหาข้อมูลเกี่ยวกับวิธีการอื่นที่อยู่รอบ ๆ ในโพสต์บล็อกC # XmlSerializer FileNotFound ยกเว้น (ซึ่งกล่าวถึงคริสขายเครื่องมือXmlSerializerPreCompiler )


162
หนึ่งในวิธีที่เป็นไปได้ในการกำจัดปัญหานี้คือเลือกตัวเลือก "รหัสของฉัน" ในเครื่องมือ -> ตัวเลือก -> การดีบั๊ก -> ตัวเลือกทั่วไป
Frederic

26
@Frederic: ความคิดเห็นนี้ยอดเยี่ยมมาก! ฉันกำลังนั่งอยู่ที่นี่พร้อมกับ "WTF!" แสดงออกบนใบหน้าของฉันพยายามตามล่าหาข้อยกเว้นลวงตานี้และฉันพบคำถามนี้พร้อมคำตอบ (เป็นความผิดของ Microsoft มีอะไรใหม่บ้าง) แต่ฉันไม่ต้องการปิดการใช้งานการจัดการข้อยกเว้นเพราะฉันอาจต้องการ รหัสของฉัน A +!
Kumba

27
ฉันคิดว่าคำแนะนำของ Hans ด้านล่างมีค่ามากกว่า - ใช้การเรียกใช้เมธอดอื่นที่ไม่สร้างข้อยกเว้นนี้เลย: XmlSerializer serializer = XmlSerializer.FromTypes (ใหม่ [] {typeof (MyType)}) [0];
สว่าง

3
ปัญหาคือสิ่งนี้ไม่ผ่านการทดสอบของฉันดังนั้นฉันจึงไม่สามารถ "เพิกเฉย" ข้อยกเว้นได้
Csaba Toth

16
ฉันขอโทษ แต่นี่เป็นข้อเสนอแนะที่แย่มาก FileNotFoundException เป็นหนึ่งในสิ่งที่พบได้ทั่วไปมากที่สุดในประสบการณ์ของฉันและการปิดใช้งานการรายงานข้อยกเว้นนี้เพียงแค่ถามหาปัญหาในอนาคต ดีกว่าที่จะเปิด 'Just My Code' หรือเปิดใช้งานการสร้างแอสเซมบลีอนุกรมตามที่อธิบายไว้ด้านล่าง
Quarkly

104

เช่นเดียวกับ Martin Sherburn กล่าวว่านี่เป็นพฤติกรรมปกติ ตัวสร้างของ XmlSerializer ก่อนพยายามค้นหาแอสเซมบลีชื่อ [YourAssembly] .XmlSerializers.dll ซึ่งควรประกอบด้วยคลาสที่สร้างขึ้นสำหรับการทำให้เป็นอนุกรมชนิดของคุณ เนื่องจาก DLL ดังกล่าวยังไม่ได้ถูกสร้างขึ้น (ไม่ใช่โดยค่าเริ่มต้น) FileNotFoundException จะถูกส่งออกไป เมื่อสิ่งนั้นเกิดขึ้นตัวสร้างของ XmlSerializer จะจับข้อยกเว้นนั้นและ DLL จะถูกสร้างขึ้นโดยอัตโนมัติเมื่อรันไทม์โดยตัวสร้างของ XmlSerializer (ซึ่งทำโดยการสร้างไฟล์ต้นฉบับ C # ในไดเรกทอรี% temp% ของคอมพิวเตอร์ของคุณจากนั้นรวบรวมโดยใช้คอมไพเลอร์ C # การสร้างเพิ่มเติมของ XmlSerializer สำหรับประเภทเดียวกันจะใช้ DLL ที่สร้างไว้แล้ว

UPDATE:เริ่มต้นจาก. NET 4.5 จะXmlSerializerไม่ทำการสร้างรหัสอีกต่อไปและไม่ทำการรวบรวมด้วย C # คอมไพเลอร์เพื่อสร้างชุดประกอบ serializer ที่รันไทม์เว้นแต่จะถูกบังคับอย่างชัดเจนโดยการตั้งค่าไฟล์การตั้งค่า ( useLegacySerializerGeneration ) การเปลี่ยนแปลงนี้จะลบการพึ่งพาcsc.exeและปรับปรุงประสิทธิภาพการเริ่มต้น ที่มา: . NET Framework 4.5 Readmeส่วน 1.3.8.1

ข้อยกเว้นได้รับการจัดการโดยตัวสร้างของ XmlSerializer ไม่จำเป็นต้องทำอะไรด้วยตัวเองคุณสามารถคลิก 'ดำเนินการต่อ' (F5) เพื่อดำเนินการโปรแกรมของคุณต่อไปและทุกอย่างจะเรียบร้อย หากคุณกังวลกับข้อยกเว้นในการหยุดการทำงานของโปรแกรมและเปิดตัวช่วยยกเว้นคุณจะต้องปิด 'Just My Code' หรือคุณได้ตั้งค่า FileNotFoundException ให้หยุดการทำงานเมื่อถูกโยนแทนที่จะเป็นเมื่อ 'ผู้ใช้ - ไม่สามารถจัดการได้'

ในการเปิดใช้งาน 'Just My Code' ให้ไปที่เครื่องมือ >> ตัวเลือก >> การดีบัก >> ทั่วไป >> เปิดใช้งาน Just My Code หากต้องการปิดการหยุดการทำงานเมื่อโยน FileNotFound ไปที่ Debug >> ข้อยกเว้น >> Find >> ป้อน 'FileNotFoundException' >> ยกเลิกการเลือกช่องทำเครื่องหมาย 'Thrown' จาก System.IO.FileNotFoundException


+1 สำหรับการอัปเดต: สิ่งนี้จะอธิบายพฤติกรรมที่แตกต่างกันเมื่อทำการดีบั๊กกรณีทดสอบ
mbx

3
การอัปเดตของคุณแนะนำว่าข้อยกเว้นนี้ไม่ควรเกิดขึ้นใน. NET 4.5 แต่ฉันยังคงเห็นมันอยู่
Timbo

@ Timbo: ฉันไม่เห็นว่าทำไมคุณจะไม่ได้รับข้อยกเว้นนั้นด้วย. NET 4.5 มันยังคงมองหาไฟล์และหากไฟล์หายไปFileNotFoundExceptionจะถูกโยนทิ้ง ความแตกต่างไม่ได้อยู่ในวิธีการตรวจสอบการมีอยู่ของแอสเซมบลี แต่ในการสร้างเมื่อมีการพิจารณาว่ามันหายไป ก่อนหน้านี้จะใช้การสร้างข้อความต้นฉบับ C # ด้วยการเรียกไปยังคอมไพเลอร์ C # เพื่อสร้าง IL เริ่มต้นด้วย. NET 4.5 มันปล่อย IL โดยตรงโดยไม่ต้องใช้คอมไพเลอร์
Allon Guralnek

1
ฉันแค่หวังว่า MS จะใช้สิ่งนี้ราวกับว่า (File.Exists (... )) {Load} else {Fallback} แทนที่จะลอง {Load} catch {Fallback} การควบคุมการไหลตามข้อยกเว้นมีกลิ่นไม่ดีและทำให้การแก้ไขข้อบกพร่องของฉันยากขึ้นและเปราะเกินความจำเป็น
Timbo

1
@Timbo: แบบง่าย ๆFile.Exists()อาจไม่เพียงพอ การค้นหาแอสเซมบลีไม่ใช่เรื่องง่ายรันไทม์ค้นหาในหลายตำแหน่งและฉันเชื่อว่าการเปลี่ยนแปลงลักษณะการทำงานตามสภาพแวดล้อม (แอปพลิเคชันคอนโซล vs โฮสต์อยู่ใน IIS ฯลฯ ) ฉันคิดว่าสิ่งที่ควรนำมาใช้นั้นเป็นTryLoadAssembly()สิ่งที่คล้ายกัน
Allon Guralnek

63

ในคุณสมบัติโครงการ Visual Studio (หน้า "สร้าง" ถ้าฉันจำได้ถูกต้อง) มีตัวเลือกที่ว่า "สร้างแอสเซมบลีการทำให้เป็นอนุกรม" ลองเปลี่ยนมันในโครงการที่สร้าง[ที่มีการประชุมของชนิดของฉัน]


4
ดูstackoverflow.com/a/8798289/1164966หากแอสเซมบลีการทำให้เป็นอนุกรมยังไม่ได้สร้างโดย Visual Studio
เบอนัวต์บลอนชอน

คำตอบที่ชัดเจนชัดเจนที่สุด! ฉันหวังว่าฉันจะลงคะแนนได้อีกครั้งเช่นกัน!
John Zabroski

59

มีวิธีแก้ปัญหาสำหรับสิ่งนั้น ถ้าคุณใช้

XmlSerializer lizer = XmlSerializer.FromTypes(new[] { typeof(MyType) })[0];

มันควรหลีกเลี่ยงข้อยกเว้นนั้น สิ่งนี้ใช้ได้สำหรับฉัน

คำเตือน: อย่าใช้หลายครั้งมิฉะนั้นคุณจะมีหน่วยความจำรั่ว

คุณจะรั่วไหลของหน่วยความจำอย่างบ้าคลั่งถ้าคุณใช้วิธีนี้เพื่อสร้างอินสแตนซ์ของXmlSerializerประเภทเดียวกันมากกว่าหนึ่งครั้ง!

นี่เป็นเพราะวิธีนี้ข้ามการแคชในตัวที่จัดทำXmlSerializer(type)และตัวXmlSerializer(type, defaultNameSpace)สร้าง (ตัวสร้างอื่น ๆ ทั้งหมดยังข้ามแคช)

หากคุณใช้วิธีการใด ๆ ในการสร้าง XmlSerializer ที่ไม่ได้ผ่านตัวสร้างทั้งสองนี้คุณจะต้องใช้แคชของคุณเองหรือคุณจะมีหน่วยความจำตกเลือด


44
คำเตือน:คุณจะรั่วไหลของหน่วยความจำอย่างบ้าคลั่งถ้าคุณใช้วิธีนี้เพื่อสร้างอินสแตนซ์ของXmlSerializerประเภทเดียวกันมากกว่าหนึ่งครั้ง! นี่เป็นเพราะวิธีนี้ข้ามการแคชในตัวที่จัดทำXmlSerializer(type)และตัวXmlSerializer(type, defaultNameSpace)สร้าง (ตัวสร้างอื่น ๆ ทั้งหมดยังข้ามแคช) หากคุณใช้วิธีการใด ๆ ในการสร้างสิ่งXmlSerializerที่ไม่ได้ผ่านตัวสร้างทั้งสองนี้คุณต้องสร้างแคชของคุณเองหรือคุณจะมีหน่วยความจำตกเลือด
Allon Guralnek

4
@ AllonGuralnek ดีฉันจะถูกสาป ... คุณถูกต้องอย่างแน่นอน; การขุดเพิ่มเติมผ่านทาง Reflector แสดงให้เห็นว่าในขณะที่ตรวจสอบแคชมันจะทำเช่นนั้นหลังจากสร้างชุดการทำให้เป็นอนุกรม! wtf?!?
JerKimball

4
เปลี่ยนข้อผิดพลาดที่รู้จัก: weblogs.asp.net/cschittko/archive/2005/01/14/353435.aspx
JerKimball

3
@JerKimball: หน้านั้นไม่ได้โกหกจริง ในขณะที่คุณค้นพบFromTypesจะปรากฏขึ้นเพื่อเติมแคช ดังนั้นควรเป็นวิธีที่ถูกต้องในการอุ่นเครื่องXmlSerializerแคชเปล่าในหนึ่งข้อความ (เช่นบทความแนะนำ) แต่เป็นวิธีที่ไม่ดีในการดึงข้อมูลจากมัน (ควรทำได้ผ่านตัวสร้างที่ง่ายที่สุด) ไม่ว่าในกรณีใดฉันไม่ทราบว่ามันเป็นข้อผิดพลาดฉันมักจะคิดว่าสิ่งใดก็ตามที่การรั่วไหลควรจะรั่วไหล (เช่นตัวXmlSerializerสร้างขั้นสูงกว่า) ฉันจะไม่ได้มีการพิจารณาแม้กระทั่งการใช้ตั้งแต่คุณก็สามารถทำได้FromTypes() types.Select(t => new XmlSerializer(t))
Allon Guralnek

2
@AllonGuralnek ลักษณะที่ไม่เป็นไปได้ของการใช้FromTypesจะมีการอุทธรณ์ - แม้ว่าจะมีข้อยกเว้นที่โยนทิ้งทั้งหมดมันเป็นงานที่มีราคาสูง วิธี 'แคชในแบบของคุณเอง' ดูเหมือนจะเป็นวิธีแก้ปัญหาเพียงอย่างเดียวเนื่องจากการแก้ไขที่สนับสนุนอย่างเป็นทางการเท่านั้นดูเหมือนจะอยู่ในชุดประกอบบนเว็บที่ไม่ชัดเจน (แก้ไข: ตรงไปตรงมาฉันทุกคนที่จะย้ายทุกสิ่งทุกอย่างไปยังสัญญาข้อมูล :))
JerKimball

23

ฉันพบปัญหานี้และไม่สามารถแก้ไขได้ด้วยวิธีการแก้ไขปัญหาใด ๆ ที่กล่าวถึง

ในที่สุดฉันก็พบทางออก ปรากฏว่า serializer ต้องการไม่เพียง แต่ประเภท แต่ประเภทซ้อนกันเช่นกัน เปลี่ยนสิ่งนี้:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

สำหรับสิ่งนี้:

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T).GetNestedTypes());

แก้ไขปัญหาสำหรับฉัน ไม่มีข้อยกเว้นหรืออะไรเพิ่มเติม


8
สิ่งนี้ใช้ได้สำหรับฉัน ใช้. Net4.0 รูปแบบคือvar xmlSerializer = new XmlSerializer(typeof(T), typeof(T).GetNestedTypes());
user3161729

1
สิ่งนี้ใช้ได้สำหรับฉันเช่นกัน แต่ดูเหมือนว่าจำเป็นเมื่อทำการซีเรียลไลซ์เท่านั้นไม่ใช่เมื่อดีซีเรียลไลเซอร์ บางทีมันอาจจะดูสมเหตุสมผล
SteveCinq

2
สิ่งนี้จะสร้างหน่วยความจำรั่วถ้าทำงานหลายครั้ง
Volodymyr Kotylo

9

ทางออกของฉันคือตรงไปที่การสะท้อนเพื่อสร้าง serializer การทำเช่นนี้ข้ามการโหลดไฟล์แปลก ๆ ที่ทำให้เกิดข้อยกเว้น ฉันทำสิ่งนี้ในฟังก์ชั่นตัวช่วยที่ดูแลแคชซีเรียลไลเซอร์

private static readonly Dictionary<Type,XmlSerializer> _xmlSerializerCache = new Dictionary<Type, XmlSerializer>();

public static XmlSerializer CreateDefaultXmlSerializer(Type type) 
{
    XmlSerializer serializer;
    if (_xmlSerializerCache.TryGetValue(type, out serializer))
    {
        return serializer;
    }
    else
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(type, null, null);
        serializer = new XmlSerializer(mapping);
        return _xmlSerializerCache[type] = serializer;
    }
}

2 ปัญหาที่นี่ - อันดับแรกรหัสของคุณไม่ปลอดภัยสำหรับเธรดและอันดับที่สอง (สำคัญกว่า) คุณกำลังพยายามทำซ้ำสิ่งที่รันไทม์. net ดำเนินการแล้ว (ขึ้นอยู่กับ ctor ที่คุณใช้) เช่นไม่จำเป็นต้องใช้รหัสนี้
เดฟแบล็

@DaveBlack: ใช่คำตอบของ quadfinity กับการแคชไปยัง ConcurrentDictionary จะดีกว่า
d - b

@ db จุดที่สองของฉันคือการแคชนั้นไม่จำเป็นต้องมีแม้แต่ตราบใดที่คุณใช้หนึ่งใน 2 ctors ที่เฟรมเวิร์กแคช (OP ใช้ค่าแรก) จาก MSDN: เพื่อเพิ่มประสิทธิภาพโครงสร้างพื้นฐานการทำให้เป็นอนุกรม XML จะสร้างแอสเซมบลีแบบไดนามิกเพื่อซีเรียลไลซ์และ deserialize ประเภทที่ระบุ กรอบค้นหาและนำชุดประกอบเหล่านั้นกลับมาใช้ใหม่ ลักษณะการทำงานนี้เกิดขึ้นเฉพาะเมื่อใช้ ctors ต่อไปนี้: XmlSerializer.XmlSerializer (ชนิด) XmlSerializer.XmlSerializer.XmlSerializer (ประเภทสตริง) การอ้างอิง: msdn.microsoft.com/en-us/library/ …
เดฟแบล็

@DaveBlack: ใช่ แต่ตัวสร้างเหล่านี้จะโยนและตรวจจับข้อยกเว้นภายในแม้ว่าการใช้จะใช้ได้อย่างสมบูรณ์ก็ตาม นี่เป็นสิ่งที่ไม่ดีและนี่คือเหตุผลว่าทำไม OP จึงถามคำถามตั้งแต่แรก
d - b

@ db จริง แต่สิ่งที่ฉันหมายถึงการพูด (แต่ไม่ชัดเจน - ขอโทษของฉัน) คือว่าบรรทัดเดียวของ soln ของคุณที่จำเป็นคือ 3 บรรทัดแรกในเงื่อนไขอื่น
Dave Black

8

ในการหลีกเลี่ยงข้อยกเว้นคุณต้องทำสองสิ่ง:

  1. เพิ่มแอททริบิวไปยังคลาสที่ทำให้เป็นอนุกรม (ฉันหวังว่าคุณจะสามารถเข้าถึงได้)
  2. สร้างไฟล์การทำให้เป็นอนุกรมด้วย sgen.exe

เพิ่มแอตทริบิวต์ System.Xml.Serialization.XmlSerializerAssembly ในคลาสของคุณ แทนที่ 'MyAssembly' ด้วยชื่อของชุดประกอบที่มี MyClass

[Serializable]
[XmlSerializerAssembly("MyAssembly.XmlSerializers")]
public class MyClass
{

}

สร้างไฟล์การทำให้เป็นอนุกรมโดยใช้ยูทิลิตี sgen.exe และปรับใช้กับแอสเซมบลีของคลาส

'sgen.exe MyAssembly.dll' จะสร้างไฟล์ MyAssembly.XmlSerializers.dll

การเปลี่ยนแปลงสองอย่างนี้จะทำให้. net ค้นหาแอสเซมบลีโดยตรง ฉันตรวจสอบแล้วมันทำงานบน. NET Framework 3.5 กับ Visual Studio 2008


ตกลงและมันล้มเหลวโดยไม่มีการเปลี่ยนแปลงเหล่านี้และถ้าเป็นเช่นนั้นทำไม?
34432 John Saunders

1
ฉันไม่สามารถหาเหตุผลได้ว่าทำไมโครงการของฉัน 4.0 ใน VS2012 ก็เริ่มล้มเหลว "ละเว้น" ข้อผิดพลาดไม่ใช่ตัวเลือกเนื่องจากมันเกิดขึ้นทุกครั้งที่ฉันพยายามเข้าถึง Active Directory ดังนั้นการเพิกเฉยจะหมายถึงการไม่รับรองความถูกต้อง ฉันยังคงผิดหวังอย่างมากที่ VS2012 จะไม่สร้าง DLL การทำให้เป็นอันดับให้ถูกต้องโดยอัตโนมัติ อย่างไรก็ตามขั้นตอนเหล่านี้เป็นโซลูชันที่สมบูรณ์แบบ
sfuqua

6

ข้อยกเว้นนี้สามารถถูกดักจับโดยตัวช่วยแก้จุดบกพร่องที่มีการจัดการ (MDA) ที่เรียกว่า BindingFailure

MDA นี้มีประโยชน์หากแอปพลิเคชันของคุณได้รับการออกแบบมาเพื่อจัดส่งพร้อมกับชุดประกอบการทำให้เป็นอนุกรมก่อนสร้าง เราทำสิ่งนี้เพื่อเพิ่มประสิทธิภาพสำหรับแอปพลิเคชันของเรา ช่วยให้เราสามารถตรวจสอบให้แน่ใจว่าชุดประกอบที่เป็นอนุกรมที่สร้างไว้ล่วงหน้านั้นถูกสร้างขึ้นอย่างถูกต้องโดยกระบวนการสร้างของเราและโหลดโดยแอปพลิเคชันโดยไม่ต้องสร้างขึ้นใหม่ทันที

มันไม่มีประโยชน์จริง ๆ ยกเว้นในสถานการณ์นี้เพราะอย่างที่ผู้โพสต์คนอื่นได้พูดเมื่อข้อผิดพลาดการผูกติดกับ Constructor Serializer แอสเซมบลีการทำให้เป็นอนุกรมจะถูกสร้างใหม่ที่รันไทม์ ดังนั้นคุณสามารถปิดได้


6

ฟังก์ชั่น XmlSerializer.FromTypes ไม่ได้โยนข้อยกเว้น แต่มันรั่วหน่วยความจำ นั่นเป็นเหตุผลที่คุณต้องแคช serializer ดังกล่าวสำหรับทุกประเภทเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำสำหรับทุกกรณีที่สร้างขึ้น

สร้างโรงงาน XmlSerializer ของคุณเองและใช้เพียง:

XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(MyType));

โรงงานดูเหมือนว่า:

public static class XmlSerializerFactoryNoThrow
{
    public static Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>();

    private static object SyncRootCache = new object();        

    /// <summary>
    /// //the constructor XmlSerializer.FromTypes does not throw exception, but it is said that it causes memory leaks
    /// http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor
    /// That is why I use dictionary to cache the serializers my self.
    /// </summary>
    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            //constructor XmlSerializer.FromTypes does not throw the first chance exception           
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            //serializer = XmlSerializerFactoryNoThrow.Create(type);
        }

        lock (SyncRootCache)
        {
            _cache[type] = serializer;
        }
        return serializer;
    }       
}

รุ่นที่ซับซ้อนมากขึ้นโดยไม่มีความเป็นไปได้ที่หน่วยความจำจะรั่ว (โปรดให้คนอื่นตรวจสอบรหัส):

    public static XmlSerializer Create(Type type)
    {
        XmlSerializer serializer;

        lock (SyncRootCache)
        {
            if (_cache.TryGetValue(type, out serializer))
                return serializer;
        }

        lock (type) //multiple variable of type of one type is same instance
        {
            lock (SyncRootCache)
            {
                if (_cache.TryGetValue(type, out serializer))
                    return serializer;
            }
            serializer = XmlSerializer.FromTypes(new[] { type })[0];
            lock (SyncRootCache)
            {
                _cache[type] = serializer;
            }
        }          
        return serializer;
    }       
}

คุณควรใช้ ConcurrentDictionary แทน รหัสนี้สามารถหยุดชะงัก
Behrooz

จะหยุดชะงักได้อย่างไรหากการจัดการทั้งหมดที่มีพจนานุกรมอยู่ในส่วนล็อค?
Tomas Kubes

ขอโทษฉันสับสน สิ่งที่ฉันหมายถึงคือมันสามารถแทรกไอเท็มได้มากกว่าหนึ่งครั้ง เพราะมีช่องว่างระหว่างเมื่อมันตรวจสอบการมีอยู่และเมื่อมันแทรก พจนานุกรมที่เกิดขึ้นพร้อมกันใช้การล็อคแบบสองเฟสบางชนิด (ถุง [0] และจากนั้นกระเป๋า [แฮช]]) และเก็บการอ้างอิงถึงกระเป๋าที่ต้องใส่ / บรรจุรายการที่คุณใช้งานอยู่ เร็วกว่าปลอดภัยกว่าและสะอาดกว่า
Behrooz

ใช่และไม่. คุณมีสิทธิ์ที่จะเกิดขึ้นว่าในเวลาเดียวกันหนึ่ง serializer ประเภทเดียวกันจะถูกสร้างขึ้นในสองหัวข้อในแบบคู่ขนานแล้วเพิ่มลงในพจนานุกรมสองครั้ง ในกรณีเช่นนี้เม็ดที่สองจะแทนที่เม็ดแรก แต่ส่วนล็อคจะรับประกันความปลอดภัยของเธรดและข้อเสียโดยรวมคือหน่วยความจำรั่วเล็กน้อย นี่คือการปรับประสิทธิภาพให้เหมาะสมเนื่องจากคุณไม่ต้องการเธรดหนึ่งที่มี Serializer ประเภท A รอถูกบล็อกโดยเธรดที่สองด้วย serializer ประเภท B ในสถานการณ์จริง
Tomas Kubes

ฉันสามารถจินตนาการถึงวิธีแก้ปัญหาอาจจะดีกว่า (โดยไม่มีการรั่วไหลของหน่วยความจำทางทฤษฎี) แต่มีความซับซ้อนมากขึ้น
Tomas Kubes

3

การแก้ไขปัญหาข้อผิดพลาดในการรวบรวมในทางกลับกันนั้นซับซ้อนมาก ปัญหาเหล่านี้ปรากฏใน FileNotFoundException ด้วยข้อความ:

File or assembly name abcdef.dll, or one of its dependencies, was not found. File name: "abcdef.dll"
   at System.Reflection.Assembly.nLoad( ... )
   at System.Reflection.Assembly.InternalLoad( ... )
   at System.Reflection.Assembly.Load(...)
   at System.CodeDom.Compiler.CompilerResults.get_CompiledAssembly() 

คุณอาจสงสัยว่าไฟล์ที่ไม่พบข้อยกเว้นเกี่ยวข้องกับ instantiating วัตถุ serializer แต่จำไว้ว่า: คอนสตรัคเตอร์เขียนไฟล์ C # และพยายามรวบรวมมัน สแตกการเรียกของข้อยกเว้นนี้ให้ข้อมูลที่ดีบางอย่างเพื่อสนับสนุนข้อสงสัยนั้น เกิดข้อยกเว้นขณะที่ XmlSerializer พยายามโหลดแอสเซมบลีที่สร้างโดย CodeDOM เรียกเมธอด System.Reflection.Assembly.Load ข้อยกเว้นไม่ได้ให้คำอธิบายเกี่ยวกับสาเหตุที่แอสเซมบลีที่ XmlSerializer ควรจะสร้างไม่มีอยู่ โดยทั่วไปแอสเซมบลีไม่อยู่เนื่องจากการรวบรวมล้มเหลวซึ่งอาจเกิดขึ้นเนื่องจากภายใต้สถานการณ์ที่หายากคุณลักษณะการทำให้เป็นอนุกรมผลิตรหัสที่คอมไพเลอร์ C # ล้มเหลวในการคอมไพล์

หมายเหตุ ข้อผิดพลาดนี้เกิดขึ้นเมื่อ XmlSerializer ทำงานภายใต้บัญชีหรือสภาพแวดล้อมความปลอดภัยที่ไม่สามารถเข้าถึงไดเรกทอรีชั่วคราวได้

ที่มา : http://msdn.microsoft.com/en-us/library/aa302290.aspx


เขาไม่ได้ระบุว่าสิ่งนี้เกิดขึ้นตอนรันไทม์ อีกสิ่งหนึ่งที่ฉันคิดได้ก็คือคุณอาจมีความขัดแย้งของ namespace / class MyType ของคุณชื่อเต็มคืออะไร?
Zyphrax

ใช่ฉันตรวจสอบลิงค์ของคุณข้อมูลเกี่ยวกับคอนสตรัคเตอร์ แต่ก็มีประโยชน์ไม่ใช่สิ่งที่ฉันต้องการ
Irwin

5
@SpaceghostAl คุณสามารถคอมไพล์ได้ตอนรันไทม์ และนั่นคือสิ่งที่ XmlSerializer ทำ มันสร้างแบบไดนามิกที่รันไทม์แอสเซมบลีที่ (de) serialize XML สำหรับชนิดเฉพาะ ด้วยเหตุผลใดก็ตามกระบวนการนี้ล้มเหลวสำหรับ OP เป็นไปได้เนื่องจากปัญหาการอนุญาตเช่นในไดเรกทอรีชั่วคราว (อาจจะเป็นโง่ออกจากพื้นที่ดิสก์แม้.)
เลขที่

คุณแน่ใจเกี่ยวกับเรื่องนี้? ฉันค่อนข้างแน่ใจว่าสิ่งที่ทำให้เป็นอันดับได้รับการรวบรวมเป็นแอสเซมบลีที่มีชื่อ YourAssemblyName.XmlSerializers.dll ในระหว่างการสร้างไม่คอมไพล์ที่รันไทม์ สิ่งนี้อาจล้มเหลวด้วยเหตุผลทุกประเภทอย่างน้อยสิทธิ์ NTFS ทั้งหมดในโฟลเดอร์การปรับใช้
tomfanning

1
ฉันหวังว่าฉันจะสามารถโหวตได้หลายครั้ง บันทึกย่อของคุณเกี่ยวกับบัญชีที่ไม่สามารถเข้าถึงโฟลเดอร์ชั่วคราวที่ทำให้เกิดคำตอบสำหรับฉัน เมื่อฉันเพิ่มบัญชีบริการของฉันไปยังกลุ่มผู้ดูแลระบบบนเซิร์ฟเวอร์มันก็ใช้งานได้ ขอบคุณ!
บ็อบฮอร์น

2

ในคุณสมบัติของโครงการ Visual Studio มีตัวเลือกว่า "สร้างแอสเซมบลีที่เป็นอนุกรม" ลองเปิดมันสำหรับโครงการที่สร้าง [Containing Assembly of MyType]


1

คลาสที่กำหนดเองเพื่อทำให้เป็นอันดับ:

[Serializable]
public class TestClass
{
    int x = 2;
    int y = 4;
    public TestClass(){}
    public TestClass(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int TestFunction()
    {
        return x + y;
    }
}

ฉันแนบตัวอย่างรหัสแล้ว บางทีนี่อาจช่วยคุณได้

static void Main(string[] args)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(TestClass));

    MemoryStream memoryStream = new MemoryStream();
    XmlTextWriter xmlWriter = new XmlTextWriter(memoryStream, Encoding.UTF8);

    TestClass domain = new TestClass(10, 3);
    xmlSerializer.Serialize(xmlWriter, domain);
    memoryStream = (MemoryStream)xmlWriter.BaseStream;
    string xmlSerializedString = ConvertByteArray2Str(memoryStream.ToArray());

    TestClass xmlDomain = (TestClass)DeserializeObject(xmlSerializedString);

    Console.WriteLine(xmlDomain.TestFunction().ToString());
    Console.ReadLine();
}

2
-1 สำหรับการไม่ใช้บล็อกเพื่อป้องกันการรั่วไหลของทรัพยากรและสำหรับการใช้ XmlTextWriter
John Saunders

ตกลงเห็นด้วย แต่ฉันยังใช้ XmlSerializer xmlSerializer = XmlSerializer ใหม่ (typeof (TestClass)); แต่ฉันไม่ได้รับข้อยกเว้นดังกล่าว
shahjapan

1

ฉันมีปัญหาที่คล้ายกันและการละเว้นข้อยกเว้นไม่ได้ผลสำหรับฉัน รหัสของฉันเรียกการตั้งค่าของ NServiceBusConfigure.With(...).XmlSerializer()...

สิ่งที่แก้ไขให้ฉันคือการเปลี่ยนแพลตฟอร์มสำหรับโครงการของฉัน

  1. ไปที่ Build \ Configuration Manager ...
  2. ค้นหาโครงการของคุณและเปลี่ยนแพลตฟอร์ม (ในกรณีของฉันจาก x86 เป็น CPU ใด ๆ )

1

เช่นเดียวกับการอ้างอิง จากคำตอบและข้อคิดเห็นของฐานข้อมูลฉันมาพร้อมกับโซลูชันนี้ซึ่งใกล้กับโซลูชัน DB มันใช้งานได้ดีในทุกกรณีของฉันและมันปลอดภัยสำหรับเธรด ฉันไม่คิดว่าการใช้ ConcurrentDictionary จะใช้ได้

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace HQ.Util.General
{
    public class XmlSerializerHelper
    {
        private static readonly Dictionary<Type, XmlSerializer> _dictTypeToSerializer = new Dictionary<Type, XmlSerializer>();

        public static XmlSerializer GetSerializer(Type type)
        {
            lock (_dictTypeToSerializer)
            {
                XmlSerializer serializer;
                if (! _dictTypeToSerializer.TryGetValue(type, out serializer))
                {
                    var importer = new XmlReflectionImporter();
                    var mapping = importer.ImportTypeMapping(type, null, null);
                    serializer = new XmlSerializer(mapping);
                    return _dictTypeToSerializer[type] = serializer;
                }

                return serializer;
            }
        }
    }
}

การใช้งาน:

        if (File.Exists(Path))
        {
            using (XmlTextReader reader = new XmlTextReader(Path))
            {
                // XmlSerializer x  = new XmlSerializer(typeof(T));
                var x = XmlSerializerHelper.GetSerializer(typeof(T));

                try
                {
                    options = (OptionsBase<T>)x.Deserialize(reader);
                }
                catch (Exception ex)
                {
                    Log.Instance.AddEntry(LogType.LogException, "Unable to open Options file: " + Path, ex);
                }
            }
        }

0

ประเภทของคุณอาจอ้างอิงแอสเซมบลีอื่น ๆ ซึ่งไม่สามารถพบได้ในGACหรือในโฟลเดอร์ถังขยะในพื้นที่ ==> ...

"หรือการอ้างอิงอย่างใดอย่างหนึ่งระบบไม่พบไฟล์ที่ระบุ"

คุณสามารถยกตัวอย่างประเภทที่คุณต้องการทำให้เป็นอันดับหรือไม่

หมายเหตุ: ตรวจสอบให้แน่ใจว่าประเภทของคุณเป็นแบบอนุกรม


0

ผมได้รับข้อผิดพลาดเดียวกันและมันก็เนื่องจากประเภทที่ผมพยายามที่จะ deserialize ไม่ได้มีคอนสตรัค parameterless เริ่มต้น ฉันเพิ่มตัวสร้างและมันก็เริ่มทำงาน


0

ฉันมีปัญหาเดียวกันจนกระทั่งฉันใช้เครื่องมือของบุคคลที่สามเพื่อสร้าง Class จาก XSD และใช้งานได้! ฉันค้นพบว่าเครื่องมือกำลังเพิ่มรหัสพิเศษบางอย่างที่ด้านบนสุดของชั้นเรียน เมื่อฉันเพิ่มรหัสเดียวกันนี้ไปด้านบนของชั้นเรียนเดิมของฉันมันทำงาน นี่คือสิ่งที่ฉันเพิ่ม ...

#pragma warning disable
namespace MyNamespace
{
  using System;
  using System.Diagnostics;
  using System.Xml.Serialization;
  using System.Collections;
  using System.Xml.Schema;
  using System.ComponentModel;
  using System.Xml;
  using System.Collections.Generic;

  [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.6.1064.2")]
  [System.SerializableAttribute()]
  [System.Diagnostics.DebuggerStepThroughAttribute()]
  [System.ComponentModel.DesignerCategoryAttribute("code")]
  [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
  [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
  public partial class MyClassName
  {
  ...

0

เห็นคำแนะนำมากมายที่จะใช้ a ConcurrentDictionaryแต่ไม่มีตัวอย่างที่ชัดเจนเลยดังนั้นฉันจะโยนหมวกของฉันลงในการแข่งขันโซลูชันนี้ ฉันไม่ใช่นักพัฒนาที่ปลอดภัยสำหรับเธรดดังนั้นหากรหัสนี้ไม่มั่นคงโปรดแจ้งให้ผู้ที่ติดตามหลังจากทราบ

public static class XmlSerializerHelper
{
    private static readonly ConcurrentDictionary<Type, XmlSerializer> TypeSerializers = new ConcurrentDictionary<Type, XmlSerializer>();

    public static XmlSerializer GetSerializer(Type type)
    {
        return TypeSerializers.GetOrAdd(type,
        t =>
        {
            var importer = new XmlReflectionImporter();
            var mapping = importer.ImportTypeMapping(t, null, null);
            return new XmlSerializer(mapping);
        });
    }
}

ฉันเคยเห็นโพสต์อื่น ๆ ที่เกี่ยวข้องConcurrentDictionaryและLazyโหลดค่า ฉันไม่แน่ใจว่าเกี่ยวข้องกับที่นี่หรือไม่ แต่นี่คือรหัสสำหรับ:

private static readonly ConcurrentDictionary<Type, Lazy<XmlSerializer>> TypeSerializers = new ConcurrentDictionary<Type, Lazy<XmlSerializer>>();

public static XmlSerializer GetSerializer(Type type)
{
    return TypeSerializers.GetOrAdd(type,
    t =>
    {
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(t, null, null);
        var lazyResult = new Lazy<XmlSerializer>(() => new XmlSerializer(mapping), LazyThreadSafetyMode.ExecutionAndPublication);
        return lazyResult;
    }).Value;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.