Zum Inhalt

mDL - microtech Data Language

Was ist mDL?

Die microtech Data Language (mDL) ist eine Abfragesprache, mit der Sie Dateninhalte aus der microtech Software als XML-Strukturen abrufen können. Die Ergebnisse lassen sich anschließend weiterverarbeiten, transformieren oder anzeigen - etwa über einen .NET ITypedList-Connector als DataSource für ein DataGrid.

mDL-Abfragen werden über die Methode GetData des IAutoApplication7-Interfaces ausgeführt:

Result = BpApp.GetData("Adressen|Nr|[10000]|[]|[AdrNr]")

Das Ergebnis ist ein Pseudo-XML-String, den Sie durch Umschließen mit einem Root-Element zu gültigem XML machen können:

Result = "<Root>" + BpApp.GetData(MeineAbfrage) + "</Root>"

Sie können dabei beliebige Namespaces oder andere XML-relevante Elemente hinzufügen.

Einschränkungen

mDL ist eine rein lesende Abfragesprache. Es gelten folgende Einschränkungen:

  • Keine Datenstrukturänderungen möglich
  • Kein direkter Zugriff auf NestedDataSets
  • Kein Schreiben von Daten

Die Ergebnisse werden als XML bereitgestellt. Die maximale Größe eines XML-Blocks beträgt 4 GB − 1025 Byte. Größere Ergebnismengen führen zu Fehlern.

Große Datenmengen

mDL sollte nicht für große Datenmengen eingesetzt werden. Für diesen Anwendungsfall steht ein Forward-Only-Cursor mit den Kennzeichen First und Next zur Verfgung - aber auch dieser Mechanismus ist nicht für sehr große Datenmengen ausgelegt. Siehe Abschnitt Große Datenmengen (First/Next).

Aufbau einer mDL-Abfrage

Die fünf Abschnitte

Jede mDL-Abfrage besteht aus fünf Abschnitten, getrennt durch das Pipe-Zeichen |:

Datenbankname|Index|Indexwerte|Optionale Felder|Feldliste

Die Abschnitte im Einzelnen:

Datenbankname - Der Name der Datenbanktabelle, wie er auf der COM-Schnittstelle verwendet wird (z. B. Adressen, Vorgang, Artikel). Dieser Name lässt sich über die Feldliste ermitteln. Es kann nur ein Wert angegeben werden.

Index - Der Indexname, ebenfalls aus der Feldliste lesbar. Die Bezeichnungen entsprechen denen der COM-Schnittstelle. Es kann nur ein Wert angegeben werden.

Indexwerte - Ein oder mehrere Werte, die sich auf den im vorherigen Abschnitt gewählten Index beziehen. Die Werte werden in eckigen Klammern angegeben.

Optionale Felder - Keine, eine oder mehrere Optionen, die das Verhalten der Abfrage steuern (z. B. IndexRange, IndexListe, History, FullFieldList, First, Next). Auch in eckigen Klammern.

Feldliste - Die Felder, die in der Ergebnismenge enthalten sein sollen. Die Feldnamen entsprechen den FieldName-Werten in den Metadaten. Auch in eckigen Klammern, kommagetrennt.

Beispiel: Die Abfrage der Adressnummer für die Adresse 10000:

Adressen|Nr|[10000]|[]|[AdrNr]

Definition und Data im XML-Ergebnis

Das XML-Ergebnis einer mDL-Abfrage enthält zwei Sektionen: Definition und Data.

Definition enthält die Metadaten der Ergebnisfelder. Jedes Feld wird als nummeriertes Element (F1, F2, …) mit folgenden Tags beschrieben:

  • FieldName - Kurzname des Feldes, wie er intern in der microtech Software verwendet wird.
  • DisplayName - Anzeigename des Feldes, in der Regel für die Benutzeroberfläche gedacht.
  • Type - Der Datentyp des Feldes (siehe Datentypen).
  • Visible - Gibt an, ob das Feld sichtbar ist (1) oder intern (0). In den meisten Fällen ist nur das ID-Feld unsichtbar.

Data enthält die eigentlichen Datensätze. Die Feldzuordnung erfolgt über die Nummerierung: F1 aus Data gehört zur Felddefinition F1 aus Definition.

XML-Ergebnis der Beispielabfrage
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>AdrNr</FieldName>
    <DisplayName>Adressnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
</Definition>
<Data>
  <F1>Adr.1501</F1>
  <F2>10000</F2>
</Data>

Datentypen

Der Type-Tag in der Definition gibt den Datentyp eines Feldes an. Für die Arbeit mit mDL sind folgende Datentypen relevant:

Typ Beschreibung
fdaChar Einzelnes Zeichen
fdaBoolean Wahrheitswert
fdaSmallInt Kleine Ganzzahl
fdaInteger Ganzzahl
fdaSingle Gleitkommazahl (einfache Genauigkeit)
fdaDouble Gleitkommazahl (doppelte Genauigkeit)
fdaDate Datum
fdaTime Uhrzeit
fdaDateTime Datum und Uhrzeit
fdaString Zeichenkette

Weitere Typen wie fdaArray, fdaBild, fdaBlobTable, fdaInfo, fdaBlob, fdaStringList, fdaBpDataSet und fdaTable existieren, stellen jedoch fast ausschließlich binäre Formate dar und sind über mDL nicht korrekt lesbar.

Optionale Felder

Die optionalen Felder steuern, welche Datensätze die Abfrage zurückliefert und wie die Ergebnismenge aufgebaut ist.

Hinweis zu Datumswerten in Indexwerten

Alle Indexwerte werden intern über AutoField.Text zugewiesen. Das bedeutet, dass nur die textuelle Datumsdarstellung funktioniert. Numerische Datumswerte (z. B. 41860,6203166435) werden nicht korrekt interpretiert.

Richtig: 01.09.2014 0:00:01 (Format dd.mm.yyyy oder dd.mm.yyyy h:mm:ss)

Falsch: 41860,6203166435 oder 41860.6203166435

Das Komma hat in mDL außerdem eine besondere Bedeutung als Trennzeichen für Indexwerte - ein Komma im Wert selbst würde als Separator interpretiert.

IndexRange

Das Kennzeichen IndexRange liefert alle Datensätze innerhalb eines Bereichs. Es können ein bis alle Felder des Index als Von/Bis-Werte übergeben werden, getrennt durch |.

Adressen|Nr|[10000|10005]|[IndexRange]|[AdrNr]

Bei zusammengesetzten Indizes können auch mehrere Indexfelder angegeben werden:

Adressen|Status|[Interessent,10000|Interessent,10005]|[IndexRange]|[AdrNr]
XML-Ergebnis
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>AdrNr</FieldName>
    <DisplayName>Adressnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
</Definition>
<Data>
  <F1>Adr.1520</F1>
  <F2>10000</F2>
</Data>
<Data>
  <F1>Adr.1521</F1>
  <F2>10001</F2>
</Data>
<Data>
  <F1>Adr.1522</F1>
  <F2>10002</F2>
</Data>
<Data>
  <F1>Adr.1523</F1>
  <F2>10003</F2>
</Data>
<Data>
  <F1>Adr.1524</F1>
  <F2>10004</F2>
</Data>
<Data>
  <F1>Adr.1525</F1>
  <F2>10005</F2>
</Data>

IndexListe

Das Kennzeichen IndexListe gibt mehrere einzelne Datensätze anhand einer kommaseparierten Liste von Indexwerten zurück. Es kann immer nur der erste Feldwert eines Index angegeben werden.

Im Gegensatz zu den anderen Kennzeichen kann IndexListe auch innerhalb der Spezialfunktionen verwendet werden.

Adressen|Nr|[10000,10001]|[IndexListe]|[AdrNr]

Bei Verwendung von IndexListe wird automatisch ein zusätzliches Feld Datensatz in die Ergebnismenge eingefügt.

XML-Ergebnis
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>Datensatz</FieldName>
    <DisplayName>Datensatz</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>AdrNr</FieldName>
    <DisplayName>Adressnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F3>
</Definition>
<Data>
  <F1>Adr.1520</F1>
  <F2>1520</F2>
  <F3>10000</F3>
</Data>
<Data>
  <F1>Adr.1521</F1>
  <F2>1521</F2>
  <F3>10001</F3>
</Data>

History

Das Kennzeichen History erweitert eine Abfrage um die Ergebnisse der zugehörigen History- bzw. Archiv-Tabelle. Die Ergebnismenge enthält dann Datensätze aus beiden Tabellen.

Vorgang|AdrNr|[10001]|[History]|[BelegNr,AuftNr,Bez]

Zur Unterscheidung der Herkunft wird automatisch ein Feld StatusInfo eingefügt. Datensätze aus der Archiv-Tabelle erhalten dort den Wert (Archiv).

XML-Ergebnis (gekürzt)
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>Statusinfo</FieldName>
    <DisplayName>Statusinfo</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>BelegNr</FieldName>
    <DisplayName>Belegnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F3>
  <F4>
    <FieldName>Bez</FieldName>
    <DisplayName>Vorgangsbezeichnung</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F4>
</Definition>
<Data>
  <F1>Vog.1501</F1>
  <F2></F2>
  <F3>AN0500001</F3>
  <F4>Angebot</F4>
</Data>
<Data>
  <F1>VogHis.1502</F1>
  <F2>(Archiv)</F2>
  <F3>RE10700001</F3>
  <F4>Bestellung per Webshop</F4>
</Data>

FullFieldList

Das Kennzeichen FullFieldList erstellt eine Ergebnismenge, die - ähnlich wie im Im-/Export-Designer - alle Felder einer Tabelle enthält. Besonders bei Tabellen wie Adressen ist dies hilfreich, da nicht nur die direkten Adressfelder, sondern auch Felder aus den zugehörigen Anschriften einbezogen werden.

Adressen|Nr|[10000]|[FullFieldList]|[AdrNr,Re.Na2]
XML-Ergebnis
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>AdrNr</FieldName>
    <DisplayName>Adressnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>Re.Na2</FieldName>
    <DisplayName>Name 2</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F3>
</Definition>
<Data>
  <F1>Adr.1520</F1>
  <F2>10000</F2>
  <F3>Bar</F3>
</Data>

Große Datenmengen (First/Next)

Für die schrittweise Verarbeitung großer Datenmengen stehen die Kennzeichen First und Next zur Verfügung. Beide setzen voraus, dass zusätzlich Parameter im Abschnitt Optionale Felder angegeben wird.

Der erste Aufruf erfolgt mit First und liefert die Metadaten (Definition) sowie den ersten Datensatz:

Adressen||[]|[First|FullFieldList|Parameter]|[AdrNr,Re.PlzOrtInfo]
XML-Ergebnis (erster Aufruf)
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>AdrNr</FieldName>
    <DisplayName>Adressnummer</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>re.PlzOrtInfo</FieldName>
    <DisplayName>PLZ &amp; Ort</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F3>
</Definition>
<Data>
  <F1>Adr.1520</F1>
  <F2>10000</F2>
  <F3></F3>
</Data>

Alle weiteren Aufrufe verwenden Next und liefern jeweils den nächsten Datensatz ohne erneute Metadaten:

Adressen||[]|[Next|FullFieldList|Parameter]|[AdrNr,Re.PlzOrtInfo]
XML-Ergebnis (Folgeaufruf)
<Data>
  <F1>Adr.1521</F1>
  <F2>10001</F2>
  <F3>55411 Bingen am Rhein</F3>
</Data>

Ist das Ende der Tabelle erreicht, wird ein leerer String zurückgegeben.

Spezialfunktionen

Die Spezialfunktionen erweitern mDL um berechnete Ergebnismengen, die ausschließlich über mDL verfügbar sind.

Umsatzauswertung

Das Kennzeichen Umsatz gibt Umsatzdaten für Tabellen aus, die Umsatzfelder enthalten (z. B. Adressen, Artikel). Der Abfrageaufbau unterscheidet sich von Standardabfragen durch einen zusätzlichen sechsten Abschnitt für das Umsatzfeld:

Datenbankname|Index|Indexwerte|Optionale Felder|Feldliste|Umsatzfelder

Im Abschnitt Umsatzfelder wird genau ein Feld angegeben, dessen Umsätze ausgewertet werden sollen (z. B. Ums). Im Abschnitt Feldliste wird bei der Umsatzauswertung der Zeitraum angegeben (z. B. AKTJAHR).

Feldliste-Optionen im fünften Abschnitt:

Option Beschreibung
AKTJAHR Aktuelles Jahr
VORJAHR Vorjahr
Vierstellige Jahreszahl (z. B. 2008) Bestimmtes Jahr
Leer [] Standardzeitraum

Beispiele:

Adressen|Nr|[10001]|[Umsatz]|[AKTJAHR]|[Ums]
Adressen|Nr|[10001]|[Umsatz]|[VORJAHR]|[Ums]
Adressen|Nr|[10001]|[Umsatz]|[2008]|[Ums]
Adressen|Nr|[10000]|[Umsatz]|[]|[Ums]

Das Ergebnis enthält Monatsfelder (UmsJan, UmsFeb, …), den Gesamtumsatz (GesUms), Roherlöse (RohJan, …, GesRoh) und OP-Umsätze (OPUmsJan, …, GesOPUms).

Erweiterte Auswertungen: UmsAw, RohAw, MgeAw

Zusätzlich zum Basiskennzeichen Umsatz können folgende Auswertungsoptionen angegeben werden:

UmsAw - Bereitet die Umsatzdaten auf, indem die Umsätze der einzelnen Monate addiert ausgegeben werden. Ermöglicht zusätzlich die Angabe eines eigenen Zeitraums.

RohAw - Wie UmsAw, jedoch für Roherlöse.

MgeAw - Wie UmsAw, jedoch für Mengenumsätze. Diese Auswertung ist nur für Felder mit Mengenumsätzen verfügbar.

Adressen|Nr|[10000]|[Umsatz|UmsAw|RohAw]|[]|[Ums]
Artikel|Nr|[Artikel 1]|[Umsatz|UmsAw|RohAw|MgeAw]|[]|[Ums]

Zusätzliche Optionen bei Angabe der Feldliste (nur in Kombination mit UmsAw/RohAw/MgeAw):

Option Beschreibung
VOR 12 Monate des Vorjahres
JJJJ-MM-TT-/-JJJJ-MM-TT Eigener Zeitraum
Adressen|Nr|[10000]|[Umsatz|UmsAw|RohAw]|[VOR]|[Ums]
Adressen|Nr|[10000]|[Umsatz|UmsAw|RohAw]|[2008-01-01-/-2008-03-31]|[Ums]

SummenDatensatz

Das Kennzeichen SummenDatensatz liefert einen summierten Wert für die Umsatzauswertungen. Es ist ausschließlich in Kombination mit den Spezialfunktionen verfügbar.

Adressen|Nr|[10001]|[SummenDatensatz|Umsatz|UmsAw|RohAw]|[2008-01-01-/-2008-03-30]|[Ums]
XML-Ergebnis
<Definition>
  <F1>
    <FieldName>Zeitraum</FieldName>
    <DisplayName>Zeitraum</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F1>
  <F2>
    <FieldName>Umsatz</FieldName>
    <DisplayName>Umsatz</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>Roherloes</FieldName>
    <DisplayName>Roherlös</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F3>
  <F4>
    <FieldName>Anteil</FieldName>
    <DisplayName>Anteil</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F4>
</Definition>
<Data>
  <F1>1. Quartal 2008</F1>
  <F2>2.832,25</F2>
  <F3>1.380,75</F3>
  <F4>48,75</F4>
</Data>

Vergleich (zwei Zeiträume)

Das Kennzeichen Vergleich ermöglicht die Gegenüberstellung von zwei Zeiträumen innerhalb einer Umsatzauswertung. Es wirkt sich nur auf die Spezialfunktionen aus.

Die beiden Zeiträume werden im Feldliste-Abschnitt durch | getrennt, jeweils im Format JJJJ-MM-TT-/-JJJJ-MM-TT:

Adressen|Nr|[10000]|[Vergleich|Umsatz|UmsAw|RohAw]|[2008-01-01-/-2008-03-31|2008-04-01-/-2008-06-30]|[Ums]

Das Ergebnis enthält neben den Umsatz- und Roherlöswerten des ersten Zeitraums auch die entsprechenden Vergleichswerte sowie die prozentuale Veränderung:

XML-Ergebnis
<Definition>
  <F1>
    <FieldName>ID</FieldName>
    <DisplayName>ID</DisplayName>
    <Type>fdaString</Type>
    <Visible>0</Visible>
  </F1>
  <F2>
    <FieldName>Zeitraum</FieldName>
    <DisplayName>Zeitraum</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F2>
  <F3>
    <FieldName>Umsatz</FieldName>
    <DisplayName>Umsatz</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F3>
  <F4>
    <FieldName>Roherloes</FieldName>
    <DisplayName>Roherlös</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F4>
  <F5>
    <FieldName>Vergleichszeitraum</FieldName>
    <DisplayName>Vergleichszeitraum</DisplayName>
    <Type>fdaString</Type>
    <Visible>1</Visible>
  </F5>
  <F6>
    <FieldName>UmsatzImVergleich</FieldName>
    <DisplayName>Umsatz im Vergleich</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F6>
  <F7>
    <FieldName>Umsatzveraenderung</FieldName>
    <DisplayName>Umsatzveränderung</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F7>
  <F8>
    <FieldName>RoherloesImVergleich</FieldName>
    <DisplayName>Roherlös im Vergleich</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F8>
  <F9>
    <FieldName>Roherloesveraenderung</FieldName>
    <DisplayName>Roherlösveränderung</DisplayName>
    <Type>fdaDouble</Type>
    <Visible>1</Visible>
  </F9>
</Definition>
<Data>
  <F1>Adr.1521</F1>
  <F2>1. Quartal 2008</F2>
  <F3>2.832,25</F3>
  <F4>1.380,75</F4>
  <F5>2. Quartal 2008</F5>
  <F6>0,00</F6>
  <F7>-100,00</F7>
  <F8>0,00</F8>
  <F9>0,00</F9>
</Data>

ITypedList .NET Data Provider

Der ITypedList-Provider nutzt mDL-Abfragen, um Daten unter .NET als generische DataSource bereitzustellen - etwa als Datenquelle für ein DataGridView. Der Zugriff ist rein lesend.

Verwendung

Der Provider wird über die Klasse BpTypedListProvider angesprochen. Beim Instanziieren werden die Verbindungsdaten übergeben, anschließend wird mit Refresh eine mDL-Abfrage ausgeführt und das Ergebnis als ITypedList-DataSource bereitgestellt:

Verwendung des Providers (C#)
public partial class Form1 : Form
{
    private BpTypedListProvider BpNexTData;

    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        BpNexTData = new BpTypedListProvider(
            "ConnectionName",
            "ConnectionKey",
            "Anmeldename",
            "Password",
            "MandantNr"
        );
        BpNexTData.Refresh("Adressen|Nr|[10000,10001]|[IndexListe]|[AdrNr]");
        dataGridView1.DataSource = BpNexTData.DataSource();
    }
}

C#-Beispiel

Das folgende Listing zeigt die vollständige Implementierung des ITypedList-Providers, bestehend aus der TypedList<T>-Klasse, dem BpNexTPropertyDescriptor, dem BpXmlReader zum Parsen der mDL-XML-Ergebnisse sowie dem BpTypedListProvider als Fassade.

Vollständiges C#-Listing: ITypedList-Provider

ITypedList Implementation

namespace BpNexT.TypedList
{
    [Serializable()]
    class TypedList<T> : BindingList<T>, ITypedList
    {
        [NonSerialized()]
        private PropertyDescriptorCollection _Properties;

        internal TypedList(FieldInfo[] descriptorInfos)
        {
            List<BpNexTPropertyDescriptor> Descripts = new List<BpNexTPropertyDescriptor>();
            BpNexTPropertyDescriptor PropertyDesc;
            int Index = 0;
            foreach (FieldInfo Fi in descriptorInfos)
            {
                PropertyDesc = new BpNexTPropertyDescriptor(Index++, Fi.Caption, Fi.FieldTypeDotNet);
                Descripts.Add(PropertyDesc);
            }
            _Properties = new PropertyDescriptorCollection(Descripts.ToArray(), true);
        }

        public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
        {
            return listAccessors == null ? _Properties :
                ListBindingHelper.GetListItemProperties(listAccessors[0].PropertyType);
        }

        public string GetListName(PropertyDescriptor[] listAccessors)
        {
            return typeof(T).Name;
        }
    }
}

PropertyDescriptor Implementation

namespace BpNexT.TypedList
{
    internal class BpNexTPropertyDescriptor : PropertyDescriptor
    {
        int _Index;
        string _DisplayName;
        Type _TypeOf;

        public BpNexTPropertyDescriptor(int index, string displayName, Type typeOf)
            : base(displayName, null)
        {
            _Index = index;
            _DisplayName = displayName;
            _TypeOf = typeOf;
        }

        public override bool CanResetValue(object component) { return false; }
        public override Type ComponentType { get { return typeof(DataObject); } }
        public override string DisplayName { get { return _DisplayName; } }
        public override object GetValue(object component)
        {
            return ((DataObject)component)[_Index];
        }
        public override bool IsReadOnly { get { return true; } }
        public override Type PropertyType { get { return _TypeOf; } }
        public override void ResetValue(object component) { }
        public override void SetValue(object component, object value) { }
        public override bool ShouldSerializeValue(object component) { return true; }
    }
}

XmlReader Implementation

namespace BpNexT.BpXmlReader
{
    class BpXmlReader
    {
        const string csDefinition = "Definition";
        const string csFieldName = "FieldName";
        const string csDisplayName = "DisplayName";
        const string csType = "Type";
        const string csVisible = "Visible";
        const string csData = "Data";
        const string csField = "F";

        private TypedList<DataObject> _DataProvider;
        private List<FieldInfo> _FieldInfo;
        private string _Input;
        private bool _Error = false;
        private string _ErrorText;

        public string InputAsString { get { return _Input; } }
        public bool Error { get { return _Error; } }
        public string ErrorText { get { return _ErrorText; } }
        public TypedList<DataObject> DataProvider { get { return _DataProvider; } }
        public List<FieldInfo> FieldInfo { get { return _FieldInfo; } }

        public BpXmlReader(string Input, bool IsImport)
        {
            try
            {
                ReadDataSet(Input, IsImport);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.Write(
                    "## Exception :: public BpXmlReader " + ex.Message);
            }
        }

        private Boolean GoTo(XmlReader reader, string Name)
        {
            try
            {
                if (reader.LocalName == Name) { return true; }
                while (reader.Read())
                {
                    if (reader.LocalName == Name) { return true; }
                }
            }
            catch { }
            return false;
        }

        private void CreateExceptionMeld(XmlReader reader)
        {
            if (_FieldInfo == null) { _FieldInfo = new List<FieldInfo>(); }
            _Error = (GoTo(reader, csFieldName) &&
                      (reader.ReadString() == "ExceptionMeld"));
            if (_Error)
            {
                if (GoTo(reader, csData))
                {
                    reader.ReadStartElement();
                    _ErrorText = reader.ReadString();
                    _ErrorText = reader.ReadString();
                }
            }
        }

        private void CreateFieldInfo(XmlReader reader)
        {
            if (_FieldInfo == null) { _FieldInfo = new List<FieldInfo>(); }
            if (GoTo(reader, csDefinition) == false) { return; }
            _FieldInfo.Clear();
            while (reader.Read())
            {
                FieldInfo Field = new FieldInfo();
                if (GoTo(reader, csFieldName) == false)
                {
                    if (reader.EOF) { return; }
                }
                Field.Name = reader.ReadString();
                if (GoTo(reader, csDisplayName) == false) { }
                Field.Caption = reader.ReadString();
                if (GoTo(reader, csType) == false) { }
                Field.Type = Field.StringToType(reader.ReadString());
                if (GoTo(reader, csVisible) == false) { }
                Field.Visible = Field.StringToBool(reader.ReadString());
                _FieldInfo.Add(Field);
            }
        }

        private void CreateTypeList(XmlReader reader, bool IsImport)
        {
            int Spalte;
            int Counter;
            bool Ende;
            Ende = false;

            if (_DataProvider != null) { _DataProvider.Clear(); }
            else
            {
                DataObject myDataObjectTmp =
                    new DataObject(_FieldInfo.ToArray(), IsImport);
                _DataProvider =
                    new TypedList<DataObject>(myDataObjectTmp.FieldInfo);
                myDataObjectTmp = null;
            }

            while (reader.ReadState != ReadState.EndOfFile)
            {
                if (GoTo(reader, csData) == false) { return; }
                DataObject myDataObject =
                    new DataObject(_FieldInfo.ToArray(), IsImport);
                Counter = myDataObject.FieldCount();

                if (_FieldInfo[0].Name == "ID")
                {
                    if (GoTo(reader, csField + (1).ToString()))
                    {
                        string buffer = reader.ReadString();
                        if (buffer != "")
                        { myDataObject.ROWmDL = "[FktEinsehen]||[" + buffer + "]"; }
                        else
                        { myDataObject.ROWmDL = ""; }
                    }
                }

                for (Spalte = 0; Spalte < Counter; Spalte++)
                {
                    if (GoTo(reader, csField +
                        ((myDataObject.ToOriginFieldPos(Spalte) + 1).ToString())) == false)
                    {
                        Ende = true;
                        break;
                    }
                    string Buffer;
                    switch (_FieldInfo.ToArray()
                        [myDataObject.ToOriginFieldPos(Spalte)].Type)
                    {
                        case BpNexT.TypedList.FieldInfo.FieldType.String:
                            Buffer = reader.ReadString();
                            if (Buffer == "Januar 1950 - Januar 2049")
                            { Buffer = "(alle)"; }
                            myDataObject[Spalte] = Buffer;
                            break;
                        case BpNexT.TypedList.FieldInfo.FieldType.Integer:
                            Buffer = reader.ReadString();
                            if (Buffer == "") { Buffer = "0"; }
                            myDataObject[Spalte] = int.Parse(Buffer);
                            break;
                        case BpNexT.TypedList.FieldInfo.FieldType.Float:
                            Buffer = reader.ReadString();
                            if (Buffer == "") { Buffer = "0"; }
                            myDataObject[Spalte] = float.Parse(Buffer);
                            break;
                        case BpNexT.TypedList.FieldInfo.FieldType.DateTime:
                            Buffer = reader.ReadString();
                            if ((Buffer == "") | (Buffer == "0"))
                            {
                                System.DateTime locBuffer;
                                System.DateTime.TryParse("0", out locBuffer);
                                myDataObject[Spalte] = locBuffer;
                            }
                            else
                            { myDataObject[Spalte] = DateTime.Parse(Buffer); }
                            break;
                        case BpNexT.TypedList.FieldInfo.FieldType.Boolean:
                            Buffer = reader.ReadString();
                            if (Buffer == "") { Buffer = "Nein"; }
                            myDataObject[Spalte] =
                                ((Buffer == "Nein") ? false : true);
                            break;
                    }
                }
                if (Ende == false)
                {
                    if (myDataObject != null)
                    { DataProvider.Add(myDataObject); }
                }
            }
        }

        private void ReadDataSet(string Input, bool IsImport)
        {
            _Error = false;
            if (Input == null)
            {
                _Error = true;
                throw new Exception(
                    "BpXmlReader.ReadDataSet Input String = \"\"");
            }
            Input = "<root>" + Input + "</root>";
            _Input = Input;
            XmlReader reader =
                XmlReader.Create(new StringReader(_Input));
            CreateExceptionMeld(reader);
            reader = null;
            reader = XmlReader.Create(new StringReader(_Input));
            reader.Read();
            CreateFieldInfo(reader);
            reader = null;
            reader = XmlReader.Create(new StringReader(_Input));
            CreateTypeList(reader, IsImport);
        }
    }
}

Implementation des Providers

namespace BpNexT
{
    public class BpTypedListProvider
    {
        private BpXmlReader.BpXmlReader _BpReader;
        private BpConnection _Connection;

        public BpTypedListProvider(
            string ConnectionName, string ConnectionKey,
            string Username, string Password, string MandNr)
        {
            try
            {
                _Connection = new BpConnection();
                _Connection.Connect(
                    ConnectionName, ConnectionKey,
                    Username, Password, MandNr);
            }
            catch
            {
                if (_Connection != null) { _Connection.Dispose(); }
            }
        }

        public void Refresh(string mDL)
        {
            _BpReader = null;
            _BpReader = new BpNexT.BpXmlReader.BpXmlReader(
                _Connection.GetData(mDL), false);
        }

        public bool Error()
        {
            bool Erg = false;
            if (_BpReader.Error) { Erg = true; }
            return Erg;
        }

        public ITypedList DataSource()
        {
            return _BpReader.DataProvider;
        }
    }
}

Verwendung des Providers

namespace WindowsApplication1
{
    public partial class Form1 : Form
    {
        private BpTypedListProvider BpNexTData;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            bxEgb.Text = "Adressen|Nr|[10000,10001]|[IndexListe]|[AdrNr]";
            BpNexTData = new BpTypedListProvider(
                "ConnectionName", "ConnectionKey",
                "Anmeldename", "Password", "MandantNr");
            BpNexTData.Refresh(bxEgb.Text);
            dataGridView1.DataSource = BpNexTData.DataSource();
        }

        private void btnRefresh_Click(object sender, EventArgs e)
        {
            BpNexTData.Refresh(bxEgb.Text);
            dataGridView1.DataSource = BpNexTData.DataSource();
        }
    }
}