Vorgehensweise: Streamen von XML-Fragmenten mit Zugriff auf Headerinformationen (C#)How to: Stream XML Fragments with Access to Header Information (C#)

Es kann vorkommen, dass Sie willkürlich große XML-Dateien lesen und Ihre Anwendung so schreiben müssen, dass der Arbeitsspeicherbedarf der Anwendung vorhersehbar ist.Sometimes you have to read arbitrarily large XML files, and write your application so that the memory footprint of the application is predictable. Wenn Sie versuchen, eine XML-Struktur mit einer großen XML-Datei zu füllen, ändert sich Ihre Speicherbeanspruchung proportional zur Größe der Datei, also exzessiv.If you attempt to populate an XML tree with a large XML file, your memory usage will be proportional to the size of the file—that is, excessive. Deshalb sollten Sie stattdessen ein Streamingverfahren verwenden.Therefore, you should use a streaming technique instead.

Zu diesem Zweck können Sie die Anwendung mit XmlReader schreiben.One option is to write your application using XmlReader. Es empfiehlt sich aber häufig, zum Abfragen der XML-Struktur LINQLINQ zu verwenden.However, you might want to use LINQLINQ to query the XML tree. Bei Verwendung von können Sie eine eigene benutzerdefinierte Achsenmethode schreiben.If this is the case, you can write your own custom axis method. Weitere Informationen finden Sie unter Vorgehensweise: Schreiben einer LINQ to XML-Axis-Methode (C#).For more information, see How to: Write a LINQ to XML Axis Method (C#).

Zum Schreiben einer eigenen Achsenmethode müssen Sie zunächst eine kleine Methode schreiben, die mit dem XmlReader Knoten so lange liest, bis sie zu einem der Knoten gelangt, die Sie interessieren.To write your own axis method, you write a small method that uses the XmlReader to read nodes until it reaches one of the nodes in which you are interested. Die Methode ruft dann die ReadFrom-Methode auf, die aus dem XmlReader liest und ein XML-Fragment instanziiert.The method then calls ReadFrom, which reads from the XmlReader and instantiates an XML fragment. Daraufhin wird jedes Fragment mit yield return an die Methode zurückgegeben, die Ihre benutzerdefinierte Achsenmethode aufzählt.It then yields each fragment through yield return to the method that is enumerating your custom axis method. Sie können dann LINQ-Abfragen für die benutzerdefinierte Achsenmethode schreiben.You can then write LINQ queries on your custom axis method.

Streamingverfahren eignen sich vor allem für Situationen, bei denen Sie das Quelldokument nur einmal verarbeiten müssen und bei denen die Elemente in der Reihenfolge verarbeitet werden können, in der sie im Dokument auftreten.Streaming techniques are best applied in situations where you need to process the source document only once, and you can process the elements in document order. Einige Standardabfrageoperatoren, wie OrderBy, durchlaufen ihre Quelle, erfassen alle Daten, sortieren sie und geben dann das erste Element in der Sequenz zurück.Certain standard query operators, such as OrderBy, iterate their source, collect all of the data, sort it, and then finally yield the first item in the sequence. Beachten Sie, dass Sie bei der Verwendung eines Abfrageoperators, der seine Quelle vor der Rückgabe des ersten Elements materialisiert, keine minimale Speicherbeanspruchung aufrechterhalten können.Note that if you use a query operator that materializes its source before yielding the first item, you will not retain a small memory footprint.

BeispielExample

Manchmal wird das Problem noch ein wenig interessanter.Sometimes the problem gets just a little more interesting. Im folgenden XML-Dokument muss der Benutzer Ihrer benutzerdefinierten Achsenmethode auch die Namen der Kunden kennen, zu denen die einzelnen Elemente gehören.In the following XML document, the consumer of your custom axis method also has to know the name of the customer that each item belongs to.

<?xml version="1.0" encoding="utf-8" ?>  
<Root>  
  <Customer>  
    <Name>A. Datum Corporation</Name>  
    <Item>  
      <Key>0001</Key>  
    </Item>  
    <Item>  
      <Key>0002</Key>  
    </Item>  
    <Item>  
      <Key>0003</Key>  
    </Item>  
    <Item>  
      <Key>0004</Key>  
    </Item>  
  </Customer>  
  <Customer>  
    <Name>Fabrikam, Inc.</Name>  
    <Item>  
      <Key>0005</Key>  
    </Item>  
    <Item>  
      <Key>0006</Key>  
    </Item>  
    <Item>  
      <Key>0007</Key>  
    </Item>  
    <Item>  
      <Key>0008</Key>  
    </Item>  
  </Customer>  
  <Customer>  
    <Name>Southridge Video</Name>  
    <Item>  
      <Key>0009</Key>  
    </Item>  
    <Item>  
      <Key>0010</Key>  
    </Item>  
  </Customer>  
</Root>  

Dieses Beispiel sucht auch nach diesen Headerinformationen, speichert sie und konstruiert dann eine kleine XML-Struktur, die sowohl die Headerinformationen als auch die Detailinformationen enthält, die von Ihnen aufgezählt werden.The approach that this example takes is to also watch for this header information, save the header information, and then build a small XML tree that contains both the header information and the detail that you are enumerating. Die Achsenmethode gibt daraufhin diese neue, kleine XML-Struktur zurück.The axis method then yields this new, small XML tree. Die Abfrage verfügt damit sowohl über Zugriff auf die Headerinformationen als auch über Zugriff auf die Detailinformationen.The query then has access to the header information as well as the detail information.

Dieser Ansatz führt zu einer geringen Speicherbeanspruchung.This approach has a small memory footprint. Da jedes XML-Detailfragment zurückgegeben wird, bleiben keine Verweise auf vorherige Fragmente erhalten. Es steht damit für die Garbage Collection (automatische Speicherbereinigung) zur Verfügung.As each detail XML fragment is yielded, no references are kept to the previous fragment, and it is available for garbage collection. Beachten Sie, dass bei diesem Verfahren viele kurzlebige Objekte auf dem Heap erstellt werden.Note that this technique creates many short lived objects on the heap.

Im folgenden Beispiel wird die Vorgehensweise bei der Implementierung und Verwendung einer benutzerdefinierten Achsenmethode gezeigt, die XML-Fragmente aus der durch den URI angegebenen Datei streamt.The following example shows how to implement and use a custom axis method that streams XML fragments from the file specified by the URI. Diese benutzerdefinierte Achse ist so geschrieben, dass sie ein Dokument mit Customer-Elementen, Name-Elementen und Item-Elementen erwartet. Diese Elemente werden wie im Dokument Source.xml oben angeordnet.This custom axis is specifically written such that it expects a document that has Customer, Name, and Item elements, and that those elements will be arranged as in the above Source.xml document. Dabei handelt es sich um eine eher einfache Implementierung.It is a simplistic implementation. Eine robustere Implementierung würde darauf vorbereitet sein, ein ungültiges Dokument analysieren zu können.A more robust implementation would be prepared to parse an invalid document.

static IEnumerable<XElement> StreamCustomerItem(string uri)  
{  
    using (XmlReader reader = XmlReader.Create(uri))  
    {  
        XElement name = null;  
        XElement item = null;  

        reader.MoveToContent();  

        // Parse the file, save header information when encountered, and yield the  
        // Item XElement objects as they are created.  

        // loop through Customer elements  
        while (reader.Read())  
        {  
            if (reader.NodeType == XmlNodeType.Element  
                && reader.Name == "Customer")  
            {  
                // move to Name element  
                while (reader.Read())  
                {  
                    if (reader.NodeType == XmlNodeType.Element &&  
                        reader.Name == "Name")  
                    {  
                        name = XElement.ReadFrom(reader) as XElement;  
                        break;  
                    }  
                }  

                // loop through Item elements  
                while (reader.Read())  
                {  
                    if (reader.NodeType == XmlNodeType.EndElement)  
                        break;  
                    if (reader.NodeType == XmlNodeType.Element  
                        && reader.Name == "Item")  
                    {  
                        item = XElement.ReadFrom(reader) as XElement;  
                        if (item != null) {  
                            XElement tempRoot = new XElement("Root",  
                                new XElement(name)  
                            );  
                            tempRoot.Add(item);  
                            yield return item;  
                        }  
                    }  
                }  
            }  
        }  
    }  
}  

static void Main(string[] args)  
{  
    XElement xmlTree = new XElement("Root",  
        from el in StreamCustomerItem("Source.xml")  
        where (int)el.Element("Key") >= 3 && (int)el.Element("Key") <= 7  
        select new XElement("Item",  
            new XElement("Customer", (string)el.Parent.Element("Name")),  
            new XElement(el.Element("Key"))  
        )  
    );  
    Console.WriteLine(xmlTree);  
}  

Dieser Code erzeugt die folgende Ausgabe:This code produces the following output:

<Root>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0003</Key>  
  </Item>  
  <Item>  
    <Customer>A. Datum Corporation</Customer>  
    <Key>0004</Key>  
  </Item>  
  <Item>  
    <Customer>Fabrikam, Inc.</Customer>  
    <Key>0005</Key>  
  </Item>  
  <Item>  
    <Customer>Fabrikam, Inc.</Customer>  
    <Key>0006</Key>  
  </Item>  
  <Item>  
    <Customer>Fabrikam, Inc.</Customer>  
    <Key>0007</Key>  
  </Item>  
</Root>  

Siehe auchSee Also

Advanced LINQ to XML Programming (C#) (Erweiterte LINQ to XML-Programmierung (C#))Advanced LINQ to XML Programming (C#)