2011-06-09 4 views
3

Nous avons un produit qui utilise 'XmlTextWriter' en C# - .NET 2.0 pour créer un grand nombre de petits fichiers XML. Ces fichiers sont ensuite lus à plusieurs reprises en utilisant 'XmlTextReader'.Les fichiers XML qui sont sérialisés à l'aide de XmlTextWriter puis lus à l'aide de XmlTextReader sont parfois corrompus

Nous avons constaté que, dans de très rares cas, sur quelques machines client, le contenu du fichier XML est remplacé par un grand nombre d'espaces blancs. Une fois que cela se produit, 'XmlTextReader' échouera évidemment à lire le fichier XML avec l'erreur "Root Element is missing".

Voici les détails logiques:

  1. Lors de l'écriture d'un nouveau fichier Xml - le fichier est d'abord écrit dans un dossier temporaire à l'aide:

    XmlTextWriter xDoc = new XmlTextWriter(Path, Encoding.UTF8); 
    
  2. Une fois que le fichier est écrit à la dossier temporaire - 'XmlTextReader' est utilisé pour vérifier le fichier.

  3. Si et seulement si le fichier est vérifié, il est copié à l'emplacement final.

  4. En quelques jours, le fichier est lu plusieurs fois à l'aide:

    XmlTextReader xDoc = new XmlTextReader(Path); 
    
  5. Dans certains rares cas, le lecteur ne parvient avec l'erreur « élément racine est manquant » et nous voyons que le fichier XML contient maintenant un certain nombre d'espaces et pas de données XML.

Voici quelques extraits de code:

Ce code est utilisé pour la sérialisation.
(Gardez à l'esprit que la sérialisation est fait dans un dossier temporaire et seulement copié sur l'emplacement final une fois que le xmlFile temporaire est vérifiée.)

  public void SerializeWithXmlTextWriter(XMLMetaData instance, string Path) 
    { 
     instance.CommitLists(); 

     #region XmlTextWriter 

     XmlTextWriter xDoc = null; 

     try 
     { 
      xDoc = new XmlTextWriter(Path, Encoding.UTF8); 
      xDoc.Formatting = Formatting.Indented; 

      xDoc.WriteProcessingInstruction("xml", "version=\"1.0\" encoding=\"utf-8\""); 
      xDoc.WriteStartElement("MD"); 
      xDoc.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance"); 
      xDoc.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema"); 


// A number of other elements are written here 

      xDoc.WriteEndElement(); 
     } 
     finally 
     { 
      if (xDoc != null) 
      { 
       xDoc.Close(); 
       xDoc = null; 
      } 
     } 

     #endregion 
    } 

Ce code est utilisé pour la désérialisation.
(il est également utilisé pour vérifier le fichier après sérialisation.)

  public XMLMetaData DeserializeWithXmlTextReader(string Path) 
    { 
     XMLMetaData instance = new XMLMetaData(); 

     #region XmlTextReader 

     XmlTextReader xDoc = null; 

     try 
     { 
      xDoc = new XmlTextReader(Path); 



      while (xDoc.Read()) 
      { 
       switch (xDoc.Name) 
       { 
        //Each element is processed using a case statement 
        //Omitted from this code sample 
       } 
      } 
     } 
     finally 
     { 
      if (xDoc != null) 
      { 
       xDoc.Close(); 
       xDoc = null; 
      } 
     } 

     #endregion 

     return instance; 
    } 

Nous avons essayé de résoudre ce problème pour un certain nombre de mois et ne peut aller nulle part, car il ne se produit que sur quelques machines clientes sur de milliers. Nous n'avons jamais été capables de le reproduire sur nos machines de développement et de test.

Nous avons eu des rapports indiquant que le problème disparaissait lorsque les applications de sauvegarde étaient arrêtées. Nous avons également un client qui semble seulement avoir des problèmes lors de l'exécution de Visual Studio.

Aussi pour les clients ayant ce problème - il semble que cela n'arrive que toutes les quelques semaines!

Merci d'avance pour votre aide :)

Simon

+0

Comme vous pouvez remarquer que je n'appelle pas Flush() avant de fermer() XmlTextWriter. Si vous regardez des exemples sur MSDN - il semble qu'il n'y a pas besoin d'appeler Flush(). [link] (http://msdn.microsoft.com/fr-fr/library/system.xml.xmltextwriter.aspx) – Xmun

+0

ne supposez pas que les exemples de code MSDN affichent les meilleures pratiques, voire fonctionnent. J'ai passé du temps à déboguer du code qui a été copié-collé à partir de MSDN avant. –

+0

@Peter - Oui, je suis entièrement d'accord avec vous. Si vous regardez ce lien [link] (http://msdn.microsoft.com/fr-fr/library/system.xml.xmltextwriter.flush.aspx), il semble que Flush() n'est nécessaire que si vous n'appelez pas Fermer(). Je vais essayer quelques tests en utilisant Flush(). Le fait est que le contenu du fichier est correct avant que la copie du fichier ait lieu.C'est plus tard que le contenu du fichier est corrompu! En outre, le code actuel échoue seulement pour une poignée de nos 5000+ utilisateurs. – Xmun

Répondre

0

Vous déclarez que le fichier est vérifié, puis copié, et que les pauses plus tard de fichiers vérifiés. Je peux voir au moins quatre possibilités:

  1. Condition de course. Le problème se produit car vous lisez le fichier en même temps que vous copiez une nouvelle version pour le remplacer.
  2. Le processus de copie est ce qui ne va pas.
  3. Le fichier a été copié avec succès, puis supprimé par un autre processus.
  4. Le fichier a été copié et supprimé par un bogue dans XmlTextReader pendant la lecture de la configuration.

La solution à 1 consiste à utiliser une synchronisation entre processus - par ex. sémaphores.

Pour étudier 2, vous pouvez vérifier le fichier après la copie. Pour être paranoïaque à propos de 3 et 4, vous pouvez vous assurer que la copie est créée en lecture seule, et pour 4, vous pouvez transmettre à XmlTextReader un FileStream ouvert en lecture seule plutôt qu'un chemin.

Si aucune de ces solutions ne vous aide, vous aurez au moins exclu quelques possibilités.

+0

1 et 2 ne sont pas possibles. Je vais mettre en œuvre le code pour protéger contre 3 et 4. Mon soupçon est que le point 4 est la cause. Je vous remercie. – Xmun

1

Nous avons rencontré le même problème au moins depuis que nous avons passés à Framework 2.0.

Nous écrivons dans un fichier temporaire en utilisant 'XmlTextWriter' et l'encodage ISO-8859-1, puis nous le copions ensuite. Nous obtenons un fichier vide, taille 0.

Nous utilisons Flush avant de fermer mais cela ne fonctionne pas non plus.

Cela arrive très rarement, nous avons environ 4000 utilisateurs et cela se produit environ une fois par mois. Hors hypothèse, il y a une erreur interne qui ne donne pas d'exception.

Nous utilisons le fichier pour les paramètres de sorte que notre solution actuelle consiste à générer des valeurs par défaut si ce problème est rencontré lors de la lecture.

Nous utilisons également le formatage. Indépendamment, c'est peut-être le coupable.

Notre code:

Public Sub Save(ByVal st As Stream, Optional ByVal AttachComment As Boolean = True) 

    Dim xtw As New XmlTextWriter(st, Text.Encoding.GetEncoding("ISO-8859-1")) 

    xtw.Formatting = Formatting.Indented 

    xtw.WriteStartDocument() 

    'Header. 
    If AttachComment Then 
     xtw.WriteComment(GenerateCreationComment()) 
    End If 

    xtw.WriteStartElement("SektionsdataFile") 

    xtw.WriteStartElement("Header") 
    WriteStringElement(xtw, "FileType", "Settings") 
    WriteStringElement(xtw, "FormatVersion", CurrentFormatVersion) 
    xtw.WriteEndElement() 

    'Settings. 
    xtw.WriteStartElement("Settings") 

    SaveSettingsCategory(xtw, Application) 
    SaveSettingsCategory(xtw, Behaviour) 
    SaveSettingsCategory(xtw, Calculation) 
    SaveSettingsCategory(xtw, Forms) 
    SaveSettingsCategory(xtw, Hardware) 
    SaveSettingsCategory(xtw, Layout) 
    SaveSettingsCategory(xtw, License) 
    SaveSettingsCategory(xtw, Paths) 
    SaveSettingsCategory(xtw, Printing) 

    xtw.WriteEndElement() 

    xtw.WriteEndElement() 

    xtw.WriteEndDocument() 

    xtw.Flush() 

    xtw.Close() 

End Sub 
+0

@AmnisJonas - merci de confirmer que ce problème se pose également à d'autres développeurs. Je pense aussi qu'il ya une sorte de bogue dans .NET 2.0 où le fichier de paramètres de user.config est corrompu d'une manière similaire. Je pensais que ce problème était limité à la classe XmlSerializer mais il semble que ce ne soit pas le cas. Nous avons eu de nombreux cas dans plusieurs produits où le fichier de paramètres de user.config est corrompu et son contenu contient simplement un certain nombre d'espaces. – Xmun

+0

Une année s'est écoulée depuis que j'ai publié ce numéro - et nous avons encore quelques problèmes avec la sérialisation XML et la corruption. Avez-vous eu de la chance de votre côté? Ce serait génial si vous pouviez nous aider si vous aviez. – Xmun

0

Tout d'abord utiliser

using(XmlWriter xmlw = XmlWriter.Create(String, XmlWriterSettings)) 
{ 
    // your code here 
} 

comme passe-partout pour la création de documents XML. Essayez-le, puis revenez s'il échoue aussi (je serais surpris). Utilisez également la construction using pour lire les XML.

L'utilisation de XmlTextReader est not recommended by Microsoft (voir les notes).

+0

merci pour le pointeur. Je vais modifier le code comme vous l'avez suggéré. Il faudra un certain temps avant que nous puissions vérifier que le problème a été résolu car il est rarement répliqué. – Xmun

+0

Cela n'a pas fonctionné pour moi non plus. Il semble qu'il y ait un bug dans la structure compacte lors de l'écriture de XML en utilisant XMLTextWriter. Je devais revenir à l'ancienne façon de l'écriture de fichiers ... en utilisant (FileStream FileStream = new FileStream (ConfigFile, FileMode.Truncate)) {XmlDocument doc = new XmlDocument(); String texte = doc.InnerXml; filestream.Write (System.Text.Encoding.UTF8.GetBytes (text), 0, text.Length); filestream.Dispose();} – JPM

Questions connexes