Thomas Kramer

IT-COW | All posts tagged 'XML'

"LINQ und Konsorten" Teil 4

By Administrator at September 02, 2010 19:28
Filed Under: 3.5, C#, Webcast-Reviews

Mittlerweile bin ich bereits beim vierten Teil der Webcast-Reihe LINQ und Konsorten von Dariusz Parys angelangt, einem Microsoft-Mitarbeiter. Mein Review zum dritten Teil der Reihe ist hier verlinkt, eventuell auch zuerst mein XML-Tutorial lesen um die Definition von XML-Elementen, XML-Attributen und Namespaces zu kennen.

 

Zuerst zeigte er das es zwei Möglichkeiten gibt um dem XML-Root-Element Child-Elemente mit Attributen anzuhängen, entweder so mit Variablen-Zuordnung:

 

using System.Xml.Linq;
public class Program
{
    static void Main(string[] args)
    {
         XElement root = new XElement("root");
         XAttribute attribute = new XAttribute("name", "dariusz");
         XElement child = new XElement("child", attribute);       

        root.Add(child);
        root.Save(Console.Out);

        Console.ReadKey();
    }

}

 

Oder es werden eben die Unter-Elemente über den Konstruktor-Aufruf des übergeordneten Elements festgelegt (dynamisch instanziiert):

 

using System.Xml.Linq
public class Program
{
    static void Main(string[] args)
    {
         XElement root = new XElement("root",
             new XElement("child",
                 new XAttribute("name", "dariusz")));

        root.Save(Console.Out);

        Console.ReadKey();
    }
}

 

Anschließend zeigte er, wie man eine LINQ to XML-Abfrage auf eine generische Personenliste gestaltet und dabei dynamisch XML-Elemente erstellt:

 

class Program
    {
        static void Main(string[] args)
        {
            List<Person> persons = new List<Person>()
            {
                new Person { Name="Dariusz" },
                new Person { Name="Daniel" },
                new Person { Name="Frank" },
                new Person { Name="Jens" },
                new Person { Name="Clemens" },
            };

            XElement root = new XElement("{http://myns}root",
                from p in persons
                select new XElement("{http://myns}child"
                    new XAttribute("name", p.Name) ),
                    new XComment("dies ist LINQ to XML"));
           
            root.Save(Console.Out); 
            Console.ReadKey();
        } 
    }

    public class Person
    {
        public string Name
        {
            get;
            set;
        }
    }

 

In dem Code wird ein XML-Root-Element erzeugt mit sovielen Child-Objekten (auf gleicher Hierarchie-Ebene) wie es Personen in der Liste gibt – der Kommentar wird ebenfalls in der zweiten Ebene eingefügt. In den {}-Klammern wird der Namespace des XML-Dokuments festgelegt, das kann alternativ auch so geschehen:

 

XNamespace ns = XNamespace.Get("http://myns");
XElement root = new XElement(ns + "root",
                          from p in persons
                          select new XElement(ns + "child",
                                       new XAttribute("name", p.Name) ),
                                           new XComment("dies ist LINQ to XML"));

 

Bei XML-Dokumenten gibt es Präfixe, um auf die Typen verschiedener XML-Schemas zugreifen zu können. So ein Präfix wird in LINQ to XML wie ein Attribut erstellt, hier als Beispiel abc:

 

XNamespace ns = XNamespace.Get("http://myns");
XElement root = new XElement(ns + "root"
                          new XAttribute(XNamespace.Xmlns + "abc", ns),
                          from p in persons
                          select new XElement(ns + "child",
                                       new XAttribute("name", p.Name) ),
                                          new XComment("dies ist LINQ to XML"));

 

Anschließend ging er zu folgendem Beispiel über um dynamisch für den Webbrowser eine xaml-Datei (WPF) zu erstellen, die eine Übersicht über die aktuell auf dem PC laufenden Prozesse ausgibt – genaugenommen der Prozesse mit mehr als 10 Threads:

 

static void Main(string[] args)
{                    
    XElement root =
        new XElement( Xamlname( "StackPanel" ),
            from p in Process.GetProcesses()
            where p.Threads.Count > 10
            select
                new XElement( XamlName("StackPanel"),
                    new XElement( XamlName("TextBlock"), p.ProcessName),
                    new XElement( XamlName("ProgressBar"),
                        new XAttribute("Height", "0.5cm"),
                        new XAttribute("Value", p.Threads.Count))));

    root.Save(@"c:\temp\output.xaml");
    Process.Start(@"c:\temp\output.xaml");

    Console.ReadKey();
}

 

Das Beispiel habe ich so bei mir nicht zum Laufen bekommen, selbst mit dem Einbinden des System-Namespaces System.Xaml beanstandete der Compiler die Verwendung des unbekannten Typs XamlName – dabei habe ich hier immerhin die Ultimate-Version von Visual Studio 2010 im Einsatz. Die gezeigten using-Klausen hatte ich alle eingebunden, die direkten Verweise im Projekt hatte er nicht gezeigt. Bei ihm funktionierte es aber schon, das habe ich gesehen.

 

Das Beispiel hatte aber auch ein paar Schwächen: keine Sortierung nach Prozessnamen, das StackPanel-Element in WPF lässt kein Scrollen zu (Link) und die Progressbar jeweils ist zwar ganz nett anzusehen, aber was soll sie aussagen? Bei mehr als 100 Threads für einen Prozess gibt es keine Differenzierung in der Anzeige mehr, da sie für prozentuale Werte gedacht ist.

 

Aber als Beispiel ist es schon ganz nett anzusehen, vor allem wie einfach die WPF-Datei zu erstellen ist. Also hier meine (geringfügig) verbesserte (und funktionierende) Version - mit namentlicher Sortierung, Scrollmöglichkeit nach unten und zusätzlicher Ausgabe der Threadanzahl pro Prozess:

 

static void Main(string[] args)
{
    XNamespace ns = XNamespace.Get("http://schemas.microsoft.com/winfx/2006/xaml/presentation");

    XElement root = new XElement((ns + "ScrollViewer"),
        new XElement((ns + "StackPanel"),
            from p in Process.GetProcesses()
            where p.Threads.Count > 10
            orderby p.ProcessName
            select
                new XElement((ns + "StackPanel"),
                    new XElement((ns + "TextBlock"), p.ProcessName + " (Threads: " + p.Threads.Count +")"),
                    new XElement((ns + "ProgressBar"),
                        new XAttribute("Height", "0.5cm"),
                        new XAttribute("Value", p.Threads.Count)))));

    root.Save(@"c:\temp\output.xaml");
    Process.Start(@"c:\temp\output.xaml");

    Console.ReadKey();
}

 

Der Namespace muss da eingefügt werden, ansonsten wird die Datei im Webbrowser nicht korrekt angezeigt weil er die verwendeten Elemente nicht kennt. Process.Start() öffnet das erstellte WPF-Dokument im Standard-Webbrowser (IE und Firefox zeigen die Datei korrekt an), nachfolgend ein Bild der Ausgabe:

 

1

 

Danach zeigte er, wie man eine LINQ to XML-Abfrage auf ein geladenes XML-Dokument anwendet. Die Erstellung der verwendeten Beispiel-Datei data.xml wurde nicht gezeigt und gibt es auch nicht zum Download, entsprechend habe ich sie selbst erstellt.

 

Aus dem SQL Server Management Studio kann man das Ergebnis von SQL-Abfragen als CSV-Datei exportieren (leider nicht direkt als XML). Ich habe dazu u. a. die Orders-Tabelle der Northwind-Datenbank auslesen lassen und das Ergebnis als CSV-Datei gespeichert. Das geht wie gewünscht nur über die Text-Ausgabe, bei der Raster-Ausgabe kann kein anderes Trennzeichen als das Semikolon eingestellt werden. Folgend die SQL-Abfrage:

 

select a1.CustomerID, a1.CompanyName, a2.OrderID, a3.ProductID,a3.Quantity,a3.UnitPrice
from Customers a1, Orders a2, [Order Details] a3
where a1.CustomerID=a2.CustomerID and a2.OrderID=a3.OrderID
group by a1.CustomerID, a1.CompanyName,a2.OrderID,a3.ProductID, a3.Quantity,a3.UnitPrice
order by a1.CustomerID, a1.CompanyName,a2.OrderID,a3.ProductID;

 

Nebenbei ist es natürlich suboptimal, Leerzeichen in einem Tabellennamen zu vergeben (Order Details). Aber die Northwind-Datenbank ist eigentlich auch für SQL Server 2000 gedacht und entsprechend schon etwas älter. Jedenfalls muss der Tabellenname deswegen mit [] umklammert werden.

 

Der MS Log Parser kann nun als Konverter dienen, mit dem folgenden Befehl kann die CSV-Datei in eine XML-Datei umgewandelt werden:

 

logparser –i:CSV –o:XML “select * into Data.xml from Data.csv” –fixedFields:ON –structure:1 –headerRow:ON

 

Wichtig hierbei ist das die erste Zeile mit den Spaltenbezeichnern vorhanden ist - für die Bezeichnung der einzelnen Spalten (headerRow). Angenommen werden vom Log Parser nur CSV-Dateien mit Komma als Trennzeichen, die Bezeichnung Comma Separated Values nimmt er also wörtlich.

 

FixedFields ist wichtig weil die letzte Spalte in der Tabelle (ShipCountry) auch Kommas enthalten kann, für diese würde er sonst weitere Felder im XML-Dokument einfügen. Der MS Log Parser kann die XML-Datei übrigens in vier geringfügig verschiedenen Formaten erstellen (structure).

 

Anschließend habe ich dann die so generierte XML-Datei eingelesen und die Anzahl der Bestellungen ausgegeben (ähnlich wie im Video) – jede Zeile in der Orders-Tabelle steht für eine Bestellung.

 

XElement xml = XElement.Load(@"c:\users\thomas\documents\Data.xml");
int numberOfOrders = (from e in xml.Descendants("ROW")
                      select e).Count();

Console.WriteLine("Anzahl Bestellungen: " + numberOfOrders);

Console.ReadKey();

 

Alternativ kann zum Laden des Dokuments auch der Befehl XDocument.Load benutzt werden… für das sukzessive Laden eines XML-Dokuments wird dagegen die Klasse XMLReader benutzt, diese kann auch auf Internetadressen angewandt werden.

 

Danach hatte er dann in einem Einschub die grundsätzliche Syntax von LINQ-Abfragen (mit allen Optionen) gezeigt. Die LINQ-Abfragen funktionieren grundsätzlich auf IQueryable<T> - und IEnumerable<T> – Objekten, der Unterschied besteht darin das die Daten bei IQueryable-Objekten vollständig geladen werden und bei IEnumerable-Objekten eben nach und nach.

 

Dann hatte er seine Abfrage nochmal überarbeitet, um seine Data.xml-Datei mit den Bestellungen über eine LINQ-Abfrage abzuarbeiten. Dazu musste ich doch nochmal umdisponieren, da aufgrund der CSV nach XML-Umwandlung und der SQL-Quelle keine direkte Hierarchie mehr im Dokument vorhanden war.

 

Ihr kennt ja bestimmt SQL-Abfragen - eine Gruppierung dort wird über einen Trick erreicht, nämlich indem das übergeordnete Element der Gruppierung in die untergeordneten Elemente als Spalte mit hineingenommen – und somit jeweils wiederholt – wird.

 

Dementsprechend sieht auch die CSV-Datei aus, rein theorethisch müsste man bei einem CSV-nach-XML-Konverter den hierarchischen Aufbau wieder herstellen können – aber praktisch hat das offenbar kaum jemand bislang in diese Tools eingebaut. Ich habe mir dazu einmal (neben dem MS Log Parser) das Tool C2X-CMD samt GUI-Frontend angesehen und das Programm kann das nicht.

 

Der MS Log Parser kann das leider auch nicht, das übergeordnete Element in der XML-Datei nennt sich dann einfach ROW und alle eingelesenen Spalten werden als Child-Elemente aufgelistet – und befinden sich entsprechend auf der gleichen Hierarchie-Ebene. Entsprechend ließ sich die LINQ-Abfrage aber nicht genauso umsetzen wie im Webcast – nur für die Abfrage der Menge an Bestellungen reichte die XML-Datei wie zuerst generiert aus.

 

Mit einer LINQ-Abfrage lässt sich aber auch die Hierarchie aus einer CSV-Datei in XML korrekt wieder herstellen. Ich nehme an das er die XML-Datei auch so ähnlich erzeugt hat, aber er hatte die Abfrage dafür eben nicht gezeigt und die Datei einfach als vorhanden vorausgesetzt – da das Management Studio diese Datei offenbar nicht mit Hausmitteln generieren kann ist das didaktisch nicht ganz so gut gelungen.

 

Da ich nun auf den Konverter verzichte habe ich auch die CSV-Datei aus dem Management Studio neu erzeugt - diesmal über die Raster-Ausgabe, ohne Spaltenköpfe und mit Semikolon als Trennzeichen. Anschließend habe ich dann auf diese CSV-Datei die folgende LINQ-Abfrage ausgeführt, die gleichzeitig auch eine XML-Datei mit definiertem Aufbau erzeugt:

          

string[] orders = File.ReadAllLines(@"c:\users\thomas\documents\data.csv");

XElement root = new XElement(("Customers"),
    from p in orders
    let zeile = p.Split(';')
    let CustomerID = zeile[0]
    let Name = zeile[1]
    group zeile by new {CustomerID, Name} into x
        select new XElement("Customer", new XAttribute("Id", x.Key.CustomerID),
        new XAttribute("Name", x.Key.Name),
        new XElement(("Orders"),
        from p2 in orders
        let zeile2 = p2.Split(';')
        let CustomerID2 = zeile2[0]
        let OrderID2 = zeile2[2]
        group zeile2 by new {CustomerID2, OrderID2} into x2
        where x2.Key.CustomerID2==x.Key.CustomerID
            select new XElement("Order", new XAttribute("Id", x2.Key.OrderID2),
            from p3 in orders
            let zeile3 = p3.Split(';')
            let CustomerID3 = zeile3[0]
            let OrderID3 = zeile3[2]
            let ProductID3 = zeile3[3]
            let Quantity = zeile3[4]
            let Price = zeile3[5]
            where (CustomerID3==x.Key.CustomerID) && (OrderID3==x2.Key.OrderID2)
                select new XElement("LineItem", new XAttribute("ProductId", ProductID3),
                                                new XAttribute("Quantity", Quantity),
                                                new XAttribute("Price", Price)))) ));


root.Save(@"c:\users\thomas\documents\data.xml");

 

Übrigens, ich wollte in der Erstellungs-Abfrage die erste Gruppierungszeile zunächst so ausführen:

 

group zeile2 by new {zeile2[0], zeile2[2]} into x2

 

Aber das geht nicht, zeile2[0] und zeile2[2] müssen zuerst auf eigene Variablen “gemappt” werden. So ist es richtig:

 

let CustomerID2 = zeile2[0]
let OrderID2 = zeile2[2]
group zeile2 by new {CustomerID2, OrderID2} into x2

 

Nun die Erstellungs-Abfrage ausgeführt - und damit hatte ich dann endlich exakt die gleiche XML-Datei wie im Video gezeigt. Sicher kann man diese aber auch direkt mit einer LINQ-Abfrage auf die Datenbank generieren, aber mein erster Einfall waren eben CSV nach XML-Konverter-Programme.

 

Jedenfalls konnte ich damit endlich die von ihm gezeigte LINQ-Abfrage auf die XML-Datei ausführen:

 

XElement xml = XElement.Load(@"c:\users\thomas\documents\data.xml");

var query = from e in xml.Descendants("Orders")                                
            select new
            {
               Kunde= (string) e.Ancestors("Customer").First().Attribute("Id"),
               AnzahlAllerBestellterProdukte =
                    (int) e.Descendants("LineItem").Sum(i => (int) i.Attribute("Quantity"))
            };

foreach (var item in query)
{
    Console.WriteLine(item);
};

 

Die Abfrage bedeutet: nimm die nachfolgenden Elemente ausgehend vom XML-Element “Orders” – aber zunächst das “Orders” übergeordnete Element “Customer” - und davon die Customer-ID und speichere das als Teil eines neuen anonymen Typs mit der Bezeichnung “Kunde” (gecastet als String). Dann nimm von “Orders” ausgehend die untergeordneten Elemente “LineItem”, summiere sie über einen Lambda-Ausdruck (auf die Spalte Quantity), caste das Ergebnis als Integer und speichere es als zweiten Teil des anonymen Typs mit der Bezeichnung “AnzahlAllerBestellterProdukte”.

 

Von mir dazu noch der zusätzliche Hinweis das das so nur funktioniert, wenn die XML-Datei bereits gruppiert ist. Noch etwas eleganter ist es aber natürlich wenn man direkt vom “Customer”-Element ausgeht, so nämlich:

 

var query = from e in xml.Descendants("Customer")                                
            select new
            {
               Kunde= (string) e.FirstAttribute,
               AnzahlAllerBestellterProdukte =
                    (int) e.Descendants("LineItem").Sum(i => (int) i.Attribute("Quantity"))
            };

 

Das ausgegebene Ergebnis ist dasselbe, was auch mit der folgenden SQL-Abfrage auf Richtigkeit überprüft werden kann (nicht im Video zu sehen, sondern von mir erzeugt):

 

select 'Kunde = ' + a1.CustomerID , 'AnzahlAllerBestellterProdukte = ' + convert(varchar, SUM(a3.Quantity))
from Customers a1, Orders a2, [Order Details] a3
where a1.CustomerID=a2.CustomerID and a2.OrderID=a3.OrderID
group by a1.CustomerID
order by a1.CustomerID;

 

Nach diesem Beispiel ging er zu den grundsätzlichen Möglichkeiten zur Modifikation eines XML-Baums über, diese bestehen aus den Methoden-Aufrufen

 

XElement.Add();

XElement.Remove();

XElement.ReplaceWith();

 

Änderungen werden gespeichert mit XElement.Save() und XDocument.Save(). Anschließend zeigte er ein Beispiel dazu:

 

XElement xml = new XElement("root",
    new XElement("child1"),
    new XElement("child2"),
    new XElement("child3"));

XElement child = xml.Descendants("child2").First();

child.AddBeforeSelf(new XElement("newchild"));
child.AddAfterSelf(new XComment("funktioniert"));

child.ReplaceWith(new XElement("ichbins"));

xml.Save(Console.Out);

 

Es wird ein root-Element mit drei child-Elementen erzeugt, danach wird das zweite child-Element davon selektiert und darüber sowie darunter jeweils ein weiteres XML-Element eingefügt. Dann wird das selektierte child-Element durch ein neues Element “ichbins” ersetzt.

 

Danach wurde im Webcast ein neues Beispiel in Visual Basic gezeigt, wo die XML-Unterstützung des Editors noch ein Stück weiter vorangeschritten ist:

 

Imports <xmlns="">http://myns">

Module Module1

    Sub Main()
        Dim XML = <root>
                      <child1>
                      </child1>
                      <%= From i In Enumerable.Range(1, 5) _
                          Select <child value=<%= i %>/> %>
                  </root>
        XML.Save(Console.Out)
        Console.ReadKey()

    End Sub

End Module

 

Es wird ein root-Element mitsamt einem untergeordneten child-Element und auf gleicher Hierarchieebene (entsprechend der Tab-Einrückung) mit einer LINQ-Abfrage weitere fünf child-Elemente mit numerischen Werten erzeugt.

 

Beachtenswert ist hierbei demnach die Berücksichtigung der Tab-Einrückungen im Editor, in der Teamarbeit würde ich das aber eher als Nachteil sehen – man könnte versehentlich den Aufbau verändern. Interessant in dem Beispiel ist aber auch die etwas elegantere Einbindung von XML-Namespaces.

 

Anschließend habe ich das Äquivalent in C# dazu erstellt - mit Ergänzung der obigen xml.Descendants-Abfrage. Diese wiederum umfunktioniert als Lambda-Ausdruck, weil die Zahlen nun eben nicht mehr Bestandteil der XML-Elemente sind sondern ein eigenes Attribut darstellen (value). Das sieht dann folgendermaßen aus:

 

XElement xml = new XElement("root",
   from i in Enumerable.Range(1,5)
   select new XElement("child", new XAttribute("value",i.ToString())));


XElement child = xml.Descendants("child").Where(att => (string)att.Attribute("value") == "2").First();               

child.AddBeforeSelf(new XElement("newchild"));
child.AddAfterSelf(new XComment("funktioniert"));

child.ReplaceWith(new XElement("ichbins"));

xml.Save(Console.Out);

 

Dann kam er im Webcast auf XML-Streaming zu sprechen - zuerst erwähnte er, das ein DOM-Modell nicht für große Datenmengen geeignet ist. Für das Lesen über Streaming in XML gibt es keine “out of the Box”-Lösung in C#, aber es gibt ein Beispiel dazu in der MSDN mit yield – vermutlich meinte er das Beispiel. Dazu hatte er leider kein Code-Beispiel gezeigt.

 

Dafür zeigte er wie das Schreiben über Streaming funktioniert, dazu hatte er zunächst der Konsolenanwendung eine LINQ-to-SQL-Klasse hinzugefügt und auf diese eine Tabelle Persons aus einer generierten Beispiel-Datenbank gezogen. Da ich diese DB nicht habe benutzte ich einfach die Customer-Tabelle der Northwind-Datenbank – natürlich sind dort nicht vergleichbar viele Datensätze wie in seiner DB vorhanden.

 

Zunächst das Beispiel ohne Streaming, so wie bislang bekannt:

 

static void Main(string[] args)
{

    using (DemosDataContext ctx = new DemosDataContext())
    {
        XElement xml = new XElement("people",
            from p in ctx.Persons
            select new XElement("person",
                new XElement("id", p.CustomerID),
                new XElement("lastname", p.ContactName)));               

        Console.WriteLine("warten...");
        Console.ReadLine();

        xml.Save(@"c:\temp\delete.xml");              
    }
}

 

Und hier das umfunktionierte Beispiel mit Streaming:

 

static void Main(string[] args)
{

    using (DemosDataContext ctx = new DemosDataContext())
    {
        XStreamingElement xml = new XStreamingElement("people",
            from p in ctx.Persons
            select new XStreamingElement("person",
                new XStreamingElement("id", p.CustomerID),
                new XStreamingElement("lastname", p.ContactName)));               

        Console.WriteLine("warten...");
        Console.ReadLine();

        xml.Save(@"c:\temp\delete.xml");              
    }
}

 

Wie man sieht wurden eigentlich nur die XElement-Objekte durch XStreamingElement-Objekte ersetzt, ansonsten ist diese Erstellungs-Abfrage wie bei LINQ üblich aufgebaut.

 

Danach kam er auf XML-Validierung zu sprechen, wie sie grundsätzlich realisiert wird:

 

XDocument element = XDocument.Load(“mydata.xml”);

XmlSchemaSet schema = new XmlSchemaSet(new NameTable());

element.Validate(schema, null, true);

 

Anschließend zeigte er die Einbindung von XML-Annotationen (Kommentaren). Annotationen werden nicht serialisiert, sind also nach einer Umwandlung in einen Datenstrom oder zurück nicht mehr vorhanden. Grundsätzlich werden sie ganz einfach so hinzugefügt:

 

class Program
{
    static void Main(string[] args)
    {
        XElement root = new XElement("root",
            new XElement("child", "Dariusz"));

        XElement child = root.Descendants("child").First();

        child.AddAnnotation(new Employee
        {
            Lastname = "Parys",
            Firstname = "Dariusz"
        });

        root.Save(Console.Out);
        root.Save(@"c:\temp\delete.xml", SaveOptions.None);

        Console.WriteLine();
        Console.WriteLine();
        ZeigeAnnotationsAn(child);
        Console.ReadKey();
    }

    private static void ZeigeAnnotationsAn(XElement child)
    {
        Employee emp = child.Annotation<Employee>();
        Console.WriteLine("{0}, {1}", emp.Firstname, emp.Lastname);
    }
}

public class Employee
{
    public string Lastname
    {
        get;
        set;
    }

    public string Firstname
    {
        get;
        set;
    }
}

 

Zu beachten ist hier die Klasse Employee mit den Auto-Implemented-Properties Lastname und Firstname, diese werden über Object Initializers mit den Werten “Parys” und “Dariusz” initialisiert und das so erzeugte Objekt wird dann als Annotation dem child-Objekt hinzugefügt.

 

Danach dann der Methodenaufruf ZeigeAnnotationsAn, der entsprechend die Employee-Annotationen des übergebenen child-Elements abruft und in einem neuen Employee-Objekt speichert. Von diesem wird dann der Vor- und Nachname in der Konsole ausgegeben.

 

Dazu ist mir noch aufgefallen das es offenbar nicht möglich ist (wegen der Serialisierung) die Annotationen mit der save-Methode des XElements abzuspeichern (also auch nicht in eine Datei). Es gibt eine überladene Methode von save mit einem SaveOptions-Parameter, aber auch dieser ermöglicht das nicht. Über Code erzeugte Annotationen in XML-Objekten machen also eigentlich nur für die Übergabe bei Funktionsaufrufen Sinn.

 

Fazit: Damit bin ich nun auch mit dem vierten Teil dieser Webcast-Reihe durch, eindeutig der bislang längste Teil. Insgesamt gefällt mir LINQ immer besser, vor allem diese “Erstellungs”-Abfragen (eine LINQ-Abfrage als Bestandteil eines Konstruktor-Aufrufs – das Ausnutzen von Objekt-Arrays bei Konstruktoren) sind interessant und ersparen einiges an Arbeit.

 

Ich hoffe das Microsoft LINQ nicht so schnell durch eine neue Technik ablöst, aber eigentlich kann ich mir kaum vorstellen wie man einen Lambda-Ausdruck bspw. noch weiter abkürzen könnte - die Technik ist schon sehr elegant (intern werden die es sicher ständig überarbeiten, aber das ist ja für uns nicht relevant).

 

Außerdem hoffe ich das meine Beiträge nicht zu kritisch rüberkommen, ich finde es sehr positiv das Herr Parys sich diese Mühe macht. Die Link-Listen am Ende meiner Beiträge werde ich vermutlich nicht mehr sortieren, das lohnt den Aufwand eigentlich nicht.

 

Fortsetzung: Link.

 

Weitere Links:

 

  • LINQ to XML-Beispiele: Link
  • LINQ to XML-Einführung im dotnet-Forum: Link
  • Extensible Application Markup Language-Wikipedia: Link
  • Übersicht zu XAML in der MSDN: Link
  • Webcast-Reihe in der MSDN zu XAML-Technologien: Link
  • Gegenüberstellung von Lambda- und XPath-Abfragen (MSDN): Link
  • Webcasts über den Oberflächen-Editor Expression Blend: Link
  • Erstellung von XML-Strukturen: Link
  • Erstellen eines IQueryable-LINQ-Anbieters: Link
  • Ausführen von Streamingtransformationen von Text in XML (MSDN): Link
  • XAML-Spezifikation: Link
  • XAML-Namespaces (MSDN): Link
  • Übersicht zu LINQ in der MSDN: Link
  • WPF-Tutorial: Link
  • XMLReader.Create-Methode in der MSDN: Link
  • Validierung mittels eines XMLReaders (MSDN): Link
  • WPF-Tutorial von Norbert Eder: Link

 

Tag-Wolke

Monats-Liste