Aus XML mittels XSD.exe automatisch Schreib-Lese-Zugriffe generieren  
Frank Dzaebel, erstellt am: 21.10.06, zuletzt geändert:  30.04.07
Kategorie: XML, .NET-Version: 2.0, [Download] [Download2] [Download3] [Download4]

Hier wird gezeigt, wie man von XML automatisch zu einer Klassen-Repräsentation innerhalb des Codes kommen kann und durch ein automatisch erstelltes DataSet einen Schreib-Lese-Zugriff bekommt. Weiterführende Links dazu zum Beispiel:
- XML Schema Definition-Tool (Xsd.exe)
- Generieren einer relationalen DataSet-Struktur aus einem XML-Schema (XSD)
-
Zuordnen von XSD-Einschränkungen (XML-Schema) zu DataSet-Einschränkungen
-
Generieren von DataSet-Beziehungen aus einem XML-Schema (XSD)
- Verstehen der Wechselbeziehung zwischen Einschränkungen und Beziehungen
- Bindungsunterstützung für XML-Schema in .NET Framework
- Mit dem XmlSerializer eigene Objekte serialisieren und deserialisieren

1) XML-Datei
   
Gegeben ist eine Ausgangs-XML-Datei (Bezirke.xml) wie hier:
  
<?xml version="1.0" standalone="yes"?> <Bezirke> <GruppeVersandBezirk Name="Bezirk001" Anzahl="2000" /> <GruppeVersandBezirk Name="Bezirk002" Anzahl="1220" /> <Bezirk Name="Bezirk001"> <BezirkDaten Name="Müller" Anzahl="200" Verteilt="189" /> <BezirkDaten Name="Maier" Anzahl="600" Verteilt="456" /> <BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" /> </Bezirk> <Bezirk Name="Bezirk002"> <BezirkDaten Name="Müller" Anzahl="200" Verteilt="190" /> <BezirkDaten Name="Maier" Anzahl="600" Verteilt="456" /> <BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" /> </Bezirk> <Ergebnis SummeVerteilt="1783" /> </Bezirke>
  

2) XSD und CS-Datei erstellen
   - Öffnen des "Visual Studio Command Prompt" über Menü: Programme / 
     "Microsoft Visual Studio 2005" / "Visual Studio Tools" / "Visual Studio 2005 Command Prompt"
   - Navigieren in das Verzeichnis der XML-Datei mittels cd ..... und folgendes eingeben:
   -       xsd Bezirke.xml
   - Dadurch wird automatisch eine Bezirke.xsd erzeugt.   
  - Nun kann in VisualStudio-Projekt die Bezirke.xsd (und somit automatisch Bezirke.Designer.cs) zum Projekt hinzugefügt werden.

(Der Visual Studio Designer erzeugt die cs-Datei deswegen, weil in der xsd-Datei ein Hint: msdata:IsDataSet="true" ist. Das ist genau das, was der Befehl "xsd /l:cs /d Bezirke.xsd" tuen würde. Wenn man eine XML-Datei öffnet, dann über Menü "XML- SchemaErstellen" ein Schema erstellen würde und "IsDataSet="true" nebst Deklaration einfügen würde, so würde der Designer auch ohne Umweg über die Kommandozeile das DataSet erstellen).
  Bezirke.Designer.cs: 
 

     Es ensteht also folgendes Schema:
    
     Wenn man im DataSet "MappingType.Hidden" durch "MappingType.Attribute" ersetzt, würde später auch die Bezirk_Id-Spalte sichtbar sein, falls gewünscht.
   

3) Lesen, Schreiben und Binden der Daten in einer der Form:
    Das Lesen und Schreiben der Daten kann einfach über ReadXml und WriteXml geschehen.
    Die Daten können zum Beispiel an den DataSource eines DataGridView gebunden werden.

  using System;
  using System.Windows.Forms;
  using System.Data;
  
  namespace XmlSchreibenLesen
  {
    public partial class Form1 : Form
    {
      public Form1()
      {
        InitializeComponent();
      }
  
      Bezirke bezirke = new Bezirke();
      string xmlPfad = "../../Bezirke.xml";
  
      private void Form1_Load(object sender,EventArgs e)
      {
        bezirke.ReadXml(xmlPfad);
        foreach (DataTable dt in bezirke.Tables)
          comboBox1.Items.Add(dt.TableName);
        comboBox1.SelectedIndex = 0;
      }
  
      private void comboBox1_SelectedIndexChanged(object sender,EventArgs e)
      {
        dataGridView1.DataSource = bezirke.Tables[comboBox1.SelectedItem.ToString()];
      }
  
      private void btnSpeichern_Click(object sender,EventArgs e)
      {
        bezirke.WriteXml(xmlPfad);
      }
    }
  }




4) Zugriffe wie Ändern, Selektieren, Löschen der Daten  [Download3]
using System;
using System.Windows.Forms;
using XsdGen.xsdSchema.nameSpaceBezirkeNeu;
using ps = XsdGen.xsdSchema.nameSpaceBezirkeNeu.Bezirke;
using System.Reflection;
using System.Diagnostics;
using System.Data;

namespace XsdGen
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void Form1_Load(object sender,EventArgs e)
    {
      #region Einlesen / Ansehen ..
      string path = "../../Bezirke.xml";
      p = new ps(); p.ReadXml(path); p.WriteXml(path); //Bezirke.xml ansehen

      int lastBdIndex = p.BezirkDaten.Rows.Count - 1;
      int lastBrIndex = p.Bezirk.Rows.Count - 1;
      #endregion


      #region Fall a) "Huber" lesen und ändern.
      //IST: <BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" Bezirk_Id="1"/>
      //SOLL:<BezirkDaten Name="Huber" Anzahl="1200" Verteilt="877"  Bezirk_Id="1"/>

      /*Z.B.*/ p.BezirkDaten[lastBdIndex].Verteilt = "877";
      //Gehen Sie mit der Maus über BezirkDaten und klicken Sie auf die Lupe.
      /*Oder*/ GetBdRow(1,"Huber")[CN.Verteilt] = 876;

      p.WriteXml(path); // jetzt Bezirke.xml ansehen
      #endregion


      #region Fall b) Bezik_ID ändern
      //IST: <BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" Bezirk_Id="0"/>
      //SOLL:<BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" Bezirk_Id="1"/>

      /*Z.B.*/ // p.BezirkDaten.Rows[2][CN.Bezirk_Id] = 1;
      /*Oder*/ GetBdRow(0,"Huber")[CN.Bezirk_Id] = 1;
      p.WriteXml(path);
      #endregion


      #region Fall c) Row, Zeile löschen
      //IST:<BezirkDaten Name="Huber" Anzahl="1200" Verteilt="1111" Bezirk_Id="1"/>
      //SOLL: [nichts]

      /*Z.B.*/ // p.BezirkDaten.Rows.RemoveAt(2);
      /*oder*/ p.BezirkDaten.Rows.Remove(GetBdRow(1,"Huber"));
      #endregion
    }


    #region Methoden, Globals
    static ps p;
    /// <summary> //p.BezirkDaten.Bezirk_IdColumn-Namen</summary>
    static class CN
    {
      public static string Bezirk_Id = "Bezirk_Id";
      public static string Verteilt = "Verteilt";
      public static string Name = "Name";
    }

    private DataRow GetBdRow(int bezirk_id,string name)
    {
      string auswahl = String.Format("{0}={1} and {2}='{3}'",CN.Bezirk_Id,bezirk_id,CN.Name,name);
      DataRow[] rows = p.BezirkDaten.Select(auswahl);
      if (rows.Length == 0) throw new Exception("In Bezirk " + bezirk_id + " gibt es keinen " + name);
      return rows[0];
    }
    #endregion
  }
}