XmlSerializer-Problem



  • Hallo,
    ich habe momentan ein kleines Problem mit dem XmlSerializer.

    Mein Code sieht folgendermaßen aus:

    public class KlasseA
    {
      private List<int> _list = new List<int>();
      public List<int> List
      {
        get { return _list; }
        set { _list = value; }
      }
    
      public KlasseA()
      {
      }
    
      public int AddItem(int item)
      {
        _list.Add(item);
        return _list.IndexOf(item);
      }
    }
    
    public class KlasseB : IDisposeable
    {
      private List<KlasseA> _list = new List<KlasseA>();
      public List<KlasseA>
      {
        get { return _list; }
        set { _list = value; }
      }
    
      public KlasseB()
      {
      }
    
      public void Dispose()
      {
        XmlSerializer writer = new XmlSerializer(typeof(List<KlasseA>));
    
        using(StreamWriter file = new StreamWriter("lists.xml")
        {
          writer.Serzialize(file, _list);
        }
      }
    }
    

    Es gibt zwei verschiedene Wege die integer-Liste in KlasseA zu befüllen, einmal per Drag-Drop von Dateien aus dem Explorer und ein andermal per OpenFileDialog.
    Beides mal wird die Liste mit dem selben Code in einem BackgroundWorker befüllt:

    Drag-Drop:

    public class MainForm : Form
    {
      KlasseB _klasseB = new KlasseB();
      BackgroundWorker worker = new BackgroundWorker();
    
      public MainForm()
      {
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
      }
    
      // per Drag-Drop
      private void listView_DragDrop(object sender, DragEventArgs e)
      {
        if(e.Data.GetDataPresent(DataFormats.FileDrop))
        {
          AddIds(e.Data.GetData(DataFormats.FileDrop) as string[], __klasseB.List[0]);
        }
      }
    
      // per FileDialog
      private void button_Click(object sender, EventArgs e)
      {
        if(openFileDialog.ShowDialog(this) == DialogResult.OK)
        {
          AddIds(openFileDialog.FileNames, __klasseB.List[0]);
        }
      }
    
      private void AddIds(string[] files, KlasseA list)
      {
        AddInfo info = new AddInfo();
        info.files = files;
        info.list = list;
        worker.RunWorkerAsync(info);
      }
    
      private void worker_DoWork(object sender, DoWorkEventArgs e)
      {
        AddInfo info = (AddInfo)e.Argument;    
    
        foreach(string file in info.files)
        {
          int id = GetId(file);
          info.list.AddItem(id);
        }
      }
    }
    
    public struct AddInfo
    {
      public string[] files;
      public KlasseA list;
    }
    

    Das Problem tritt jetzt auf, wenn die Listen in der Dispose-Methode serialisiert werden sollen. Schaue ich mir den Inhalt von KlasseB.List im Debug-Modus an, sieht alles korrekt befüllt aus. Allerdings werden nun alle Daten, die per OpenFileDialog hinzugefügt wurden NICHT gespeichert. Wohingegen alle Daten die per Drag-Drop hinzugefügt wurden korrekt in der Xml-Datei landen.

    Ich kann mir bisher leider keinen Reim darauf machen, wo das Problem seine Ursache hat und noch weniger wie ich es lösen kann.

    Für Hilfe und Anregungen wäre ich sehr dankbar

    Zerebus



  • Ok, Anregungen...

    Du mißbrauchst die Dispose-Funktion. Diese ist per Definition für folgendes bestimmt:

    http://msdn2.microsoft.com/en-us/library/system.idisposable.dispose(VS.71).aspx

    Use this method to close or release unmanaged resources such as files, streams, and handles held by an instance of the class that implements this interface. This method is, by convention, used for all tasks associated with freeing resources held by an object, or preparing an object for reuse.

    Du machst da aber etwas ganz anderes, unerwartetes. Sowas ist grundsätzlich eine schlechte Idee. Viel besser einfach eine DoSerialize()-Funktion schreiben und diese explizit im OnClosing-Event der Form aufrufen.

    Ich vermute das dies auch der Grund für das Problem ist. Dispose wird von der GC aufgerufen wenn diese Objecte abbaut. (ausser Du rufst Dispose zusätzlich von Hand auf, was aus dem Code aber nicht ersichtlich ist) Zu dem Zeitpnkt wo die GC läuft kannst Du gar nichts vorraussetzen. Es könnte also sein das die Liste mit den Items schon lange von der GC abgeräumt wurde, sprich leer ist. Oder aber das der XML-Stream bereits geschlossen wurde etc. Die Reihenfolge in der die GC vorgeht ist nicht deterministisch, kann also jedesmal anders sein, so das dein Code zufällig mal funktioniert und mal nicht.



  • Zwei Sachen:

    a) Dispose() wird *nicht* automatisch vom GC aufgerufen. Dispose() wird in genau zwei Fällen aufgerufen: 1.) wenn du es explizit aufrufst und 2.) wenn du das Objekt innerhalb einer using-Anweisung erstellst (am Ende der using-Anweisung wird Dispose() für das Objekt aufgerufen).

    b) Gib uns mal den kompletten Code, ich nehme stark an, dass du den Code gekürzt hast und der in Wirklichkeit etwas anders aussieht.

    So ergibt das im Moment keinen Sinn, da wie gesagt Dispose() in dem gezeigten Code gar nicht aufgerufen würde.

    PS: Die IDisposable-Schnittstelle sollte eigentlich tatsächlich nur zum Aufräumen benutzt werden und dann aber "richtig" implementiert werden, dazu gehört z.B. auch ein Finalizer (Destruktor). Siehe MSDN-Beispiel. Wie mein Vorredner schon sagte, gehört das, was du da vorhast eigentlich nicht in den Aufräum-Code, sondern in eine eigene Funktion, die beim Beenden des Programms aufgerufen wird.



  • Danke an euch beide, aber leider lief der Vorschlag nicht Dispose() zu benutzten auch ins Leere. Aber ich habe mittlerweile den Verdächtigen etwas weiter eingekreist.
    So bald irgendwo ein OpenFileDialog ins Spiel kommt, muss nur geöffnet und per "OK"-Button im erscheinenden Feld wieder geschlossen werden (z.B. auch im DragDrop-Event) schlägt die anschließende Speicherung im FormClosing-Event fehl.

    Jemand eine Idee woran das liegen könnte?
    Ich verwende den Dialog, so wie oben in meinem Code gezeigt. Ist daran etwas falsch?

    Zerebus



  • Ich würde mal den Debugger drauf ansetzen und mir die Liste anschauen, wenn das Closing Event aufgerufen wird. Und denn würde ich Schritt für Schritt schauen was passiert.


Anmelden zum Antworten