2009-10-07 12 views
17

Ich habe derzeit ein wirklich seltsames Problem und ich kann nicht herausfinden, wie es gelöst werden kann.XmlSerializer-Leistungsproblem bei der Angabe von XmlRootAttribute

Ich habe einen ziemlich komplexen Typ, den ich versuche, mit der XmlSerializer-Klasse zu serialisieren. Dies funktioniert tatsächlich gut und der Typ serialisiert ordnungsgemäß, aber scheint eine lange Zeit damit zu verbringen; ungefähr 5 Sekunden abhängig von den Daten im Objekt.

Nach ein wenig Profiling habe ich das Problem - bizarr - auf die Angabe eines XmlRootAttribute eingeschränkt, wenn ich XmlSerializer.Serialize aufruft. Ich tue das, um den Namen einer Sammlung, die von ArrayOf serialisiert wird, in etwas Sinnvolleres zu ändern. Sobald ich den Parameter entferne, ist die Operation fast sofort!

Irgendwelche Gedanken oder Vorschläge wären ausgezeichnet, da ich auf diesem einen völlig ratlos bin!

+1

Okay, sieht aus wie das Problem ist, dass th Die Serialisierungsassembly wird für jede Serializerinstanz generiert, wenn Sie dem Serializer etwas anderes als einen Typparameter angeben! Deshalb - nehme ich an - sehe ich solche schreckliche Leistung. Kennt jemand irgendeinen Grund, warum der Standard XmlSerializer dies tun würde? Ich verstehe nicht, warum der Name des Root-Knotens nur bedeuten würde, dass der Cache nicht verwendet werden könnte? – Dougc

Antwort

23

Nur für alle anderen, die in dieses Problem läuft; unter Verwendung der folgenden Klasse bewaffnet mit der Antwort oben und das Beispiel von MSDN konnte ich dieses Problem beheben:

public static class XmlSerializerCache 
{ 
    private static readonly Dictionary<string, XmlSerializer> cache = 
          new Dictionary<string, XmlSerializer>(); 

    public static XmlSerializer Create(Type type, XmlRootAttribute root) 
    { 
     var key = String.Format(
        CultureInfo.InvariantCulture, 
        "{0}:{1}", 
        type, 
        root.ElementName); 

     if (!cache.ContainsKey(key)) 
     { 
      cache.Add(key, new XmlSerializer(type, root)); 
     } 

     return cache[key]; 
    } 
} 

statt mit dem Standard-XmlSerializer Konstruktor Dann die eine XmlRootAttribute nimmt, verwende ich die folgende Stelle:

var xmlRootAttribute = new XmlRootAttribute("ExampleElement"); 
var serializer = XmlSerializerCache.Create(target.GetType(), xmlRootAttribute); 

Meine Anwendung läuft jetzt wieder!

+0

+1, kurz und süß. –

+6

Dies ist eine leichte Optimierung. TryGet den Wert in der if-Klausel, wenn Sie viele dieser Suchvorgänge durchführen. ContainsKey ist effizienter, wenn Sie nur wissen wollen, ob das Element vorhanden ist, aber es nicht erhalten, aber da Sie immer den Wert zurückgeben, ist tryget besser: http://dotnepperls.com/dictionary-lookup –

+0

Dann können Sie auch verbessern " return "Anweisung, da Sie die Instanz bereits in der Variablen haben und nicht über" cache [key] "suchen müssen – Sielu

18

Wie in der Follow-up-Kommentar auf die ursprüngliche Frage erwähnt, gibt .NET-Assemblies, wenn XmlSerializers Erstellen und speichert die erzeugte Baugruppe, wenn sie eine dieser beiden Konstrukteure erstellt wird:

XmlSerializer(Type) 
XmlSerializer(Type, String) 

Baugruppen erzeugt Die Verwendung der anderen Konstruktoren wird nicht zwischengespeichert, daher muss .NET jedes Mal neue Assemblys generieren.

Warum? Diese Antwort ist wahrscheinlich nicht sehr befriedigend, aber wenn Sie dies in Reflector betrachten, können Sie sehen, dass der Schlüssel zum Speichern und Zugriff auf die generierten XmlSerializer Assemblies (TempAssemblyCacheKey) nur ein einfacher zusammengesetzter Schlüssel aus dem serialisierbaren Typ und (optional) ist Namensraum.

Somit gibt es keinen Mechanismus zu sagen, ob eine im Cache gespeicherte XmlSerializer für SomeType hat eine besondere XmlRootAttribute oder die Standard ein.

Es ist schwer, einen technischen Grund dafür zu finden, dass der Schlüssel nicht mehr Elemente aufnehmen kann. Dies ist wahrscheinlich nur ein Feature, das niemand implementieren konnte (insbesondere weil es ansonsten stabile Klassen ändern würde).

Möglicherweise haben Sie gesehen, aber falls Sie nicht haben, diskutiert the XmlSerializer class documentation eine Abhilfe:

Wenn Sie eine der anderen Konstrukteure verwenden, mehrere Versionen der gleichen Baugruppe erzeugt und nie entladen, was zu einem Speicher Leck und schlechte Leistung führt. Die einfachste Lösung ist die Verwendung eines der zuvor erwähnten zwei Konstruktoren. Andernfalls müssen Sie die Baugruppen in einem Hashtable, zwischenspeichern, wie im folgenden Beispiel in gezeigt.

(Ich habe das Beispiel hier weggelassen)

+0

Ausgezeichnet. Danke für deine Hilfe Jeff. Ich habe keine Ahnung, warum ich die MSDN-Dokumentation nicht gelesen habe, doh! – Dougc

+0

Gibt es Beispiele für diese HashTable, wo Sie keine Kontrolle über den Xml Serializer haben? Ich denke speziell in einer WCF Service Reference oder Web Reference? Ich habe dieses Problem, aber ich kann keine Wege finden, es zu beheben ...! – harrisonmeister

0

Es ist eine komplexere Implementierung erklärt here. Das Projekt ist jedoch nicht mehr aktiv.

die entsprechenden Klassen sind hier: http://mvpxml.codeplex.com/SourceControl/changeset/view/64156#258382

Insbesondere die folgende Funktion ein eindeutiger Schlüssel zu erzeugen, kann nützlich sein:

public static string MakeKey(Type type 
    , XmlAttributeOverrides overrides 
    , Type[] types 
    , XmlRootAttribute root 
    , String defaultNamespace) { 
    StringBuilder keyBuilder = new StringBuilder(); 
    keyBuilder.Append(type.FullName); 
    keyBuilder.Append("??"); 
    keyBuilder.Append(SignatureExtractor.GetOverridesSignature(overrides)); 
    keyBuilder.Append("??"); 
    keyBuilder.Append(SignatureExtractor.GetTypeArraySignature(types)); 
    keyBuilder.Append("??"); 
    keyBuilder.Append(SignatureExtractor.GetXmlRootSignature(root)); 
    keyBuilder.Append("??"); 
    keyBuilder.Append(SignatureExtractor.GetDefaultNamespaceSignature(defaultNamespace)); 

    return keyBuilder.ToString(); 
} 
1

Genau so etwas zu realisieren hatte verwendet und eine leicht mehr optimierte Version von @ Dougc-Lösung mit einem Komfort-Overload:

Verwandte Themen