Beispiel zu stark typisierten Erweiterungen

Für das Beispiel StronglyTypedExtensions wird die SyndicationFeed-Klasse verwendet. Die in diesem Beispiel gezeigten Muster können jedoch in allen Syndication-Klassen verwendet werden, die Erweiterungsdaten unterstützen.

Das Syndication-Objektmodell (SyndicationFeed, SyndicationItem und verwandte Klassen) unterstützt lose typisierten Zugriff auf Erweiterungsdaten mithilfe der AttributeExtensions-Eigenschaft und der ElementExtensions-Eigenschaft. In diesem Beispiel wird das Bereitstellen eines stark typisierten Zugriffs auf Erweiterungsdaten veranschaulicht, indem benutzerdefinierte, von SyndicationFeed und SyndicationItem abgeleitete Klassen implementiert werden. Sie machen bestimmte anwendungsspezifische Erweiterungen als stark typisierte Eigenschaften verfügbar.

Außerdem wird als Beispiel veranschaulicht, wie ein im vorgeschlagenen RFC zu Atom-Threading-Erweiterungen definiertes Erweiterungselement definiert wird. Dies dient nur zur Veranschaulichung. Das Beispiel ist keine vollständige Implementierung der vorgeschlagenen Spezifikation.

Beispiel-XML

Das folgende XML-Beispiel zeigt einen Atom 1.0-Eintrag mit einem zusätzlichen <in-reply-to>-Erweiterungselement.

<entry>
    <id>tag:example.org,2005:1,2</id>
    <title type="text">Another response to the original</title>
    <summary type="text">
         This is a response to the original entry</summary>
    <updated>2006-03-01T12:12:13Z</updated>
    <link href="http://www.example.org/entries/1/2" />
    <in-reply-to p3:ref="tag:example.org,2005:1"
                 p3:href="http://www.example.org/entries/1"
                 p3:type="application/xhtml+xml"
                 xmlns:p3="http://contoso.org/syndication/thread/1.0"
                 xmlns="http://contoso.org/syndication/thread/1.0">
      <anotherElement xmlns="http://www.w3.org/2005/Atom">
                     Some more data</anotherElement>
      <aDifferentElement xmlns="http://www.w3.org/2005/Atom">
                     Even more data</aDifferentElement>
    </in-reply-to>
</entry>

Das <in-reply-to>-Element gibt drei erforderliche Attribute an (ref, typeund href) und lässt außerdem das Vorhandensein weiterer Erweiterungsattribute und Erweiterungselemente zu.

Modellieren des In-Reply-To-Elements

In diesem Beispiel wird das <in-reply-to>-Element als CLR modelliert, das IXmlSerializable implementiert. Dabei wird die Verwendung mit DataContractSerializer ermöglicht. Außerdem werden einige Methoden und Eigenschaften für den Zugriff auf die Daten des Elements implementiert, wie im folgenden Beispielcode dargestellt ist.

[XmlRoot(ElementName = "in-reply-to", Namespace = "http://contoso.org/syndication/thread/1.0")]
public class InReplyToElement : IXmlSerializable
{
    internal const string ElementName = "in-reply-to";
    internal const string NsUri =
                  "http://contoso.org/syndication/thread/1.0";
    private Dictionary<XmlQualifiedName, string> extensionAttributes;
    private Collection<XElement> extensionElements;

    public InReplyToElement()
    {
        this.extensionElements = new Collection<XElement>();
        this.extensionAttributes = new Dictionary<XmlQualifiedName,
                                                          string>();
    }

    public Dictionary<XmlQualifiedName, string> AttributeExtensions
    {
        get { return this.extensionAttributes; }
    }

    public Collection<XElement> ElementExtensions
    {
        get { return this.extensionElements; }
    }

    public Uri Href
    { get; set; }

    public string MediaType
    { get; set; }

    public string Ref
    { get; set; }

    public Uri Source
    { get; set; }
}

Die InReplyToElement-Klasse implementiert Eigenschaften für das erforderliche Attribut (HRef, MediaType und Source) sowie Auflistungen für AttributeExtensions und ElementExtensions.

Die InReplyToElement-Klasse implementiert die IXmlSerializable-Schnittstelle, mit der direkt gesteuert werden kann, wie Objektinstanzen aus XML gelesen und in XML geschrieben werden. Die ReadXml-Methode liest zuerst Werte für die Eigenschaften Ref, HRef, Source und MediaType aus dem übergebenen XmlReader. Alle unbekannten Attribute werden in der AttributeExtensions-Auflistung gespeichert. Wenn alle Attribute gelesen wurden, wird ReadStartElement() aufgerufen, um den Reader an das nächste Element weiterzugeben. Da das von dieser Klasse modellierte Element keine erforderlichen untergeordneten Elemente aufweist, werden untergeordnete Elemente in XElement-Instanzen gepuffert und in der ElementExtensions-Auflistung gespeichert, wie im folgenden Code dargestellt.

public void ReadXml(System.Xml.XmlReader reader)
{
    bool isEmpty = reader.IsEmptyElement;

    if (reader.HasAttributes)
    {
        for (int i = 0; i < reader.AttributeCount; i++)
        {
            reader.MoveToNextAttribute();

            if (reader.NamespaceURI == "")
            {
                if (reader.LocalName == "ref")
                {
                    this.Ref = reader.Value;
                }
                else if (reader.LocalName == "href")
                {
                    this.Href = new Uri(reader.Value);
                }
                else if (reader.LocalName == "source")
                {
                    this.Source = new Uri(reader.Value);
                }
                else if (reader.LocalName == "type")
                {
                    this.MediaType = reader.Value;
                }
                else
                {
                    this.AttributeExtensions.Add(new
                                 XmlQualifiedName(reader.LocalName,
                                 reader.NamespaceURI),
                                 reader.Value);
                }
            }
        }
    }

    reader.ReadStartElement();

    if (!isEmpty)
    {
        while (reader.IsStartElement())
        {
            ElementExtensions.Add(
                  (XElement) XElement.ReadFrom(reader));
        }
        reader.ReadEndElement();
    }
}

In WriteXml schreibt die InReplyToElement-Methode zuerst die Werte der Eigenschaften Ref, HRef, Source und MediaType als XML-Attribute (WriteXml ist nicht zuständig für das Schreiben des tatsächlichen äußeren Elements wie beim Aufrufer von WriteXml). Außerdem wird auch der Inhalt von AttributeExtensions und ElementExtensions an den Writer geschrieben, wie im folgenden Code dargestellt.

public void WriteXml(System.Xml.XmlWriter writer)
{
    if (this.Ref != null)
    {
        writer.WriteAttributeString("ref", InReplyToElement.NsUri,
                                            this.Ref);
    }
    if (this.Href != null)
    {
        writer.WriteAttributeString("href", InReplyToElement.NsUri,
                                                this.Href.ToString());
    }
    if (this.Source != null)
    {
        writer.WriteAttributeString("source", InReplyToElement.NsUri,
                                              this.Source.ToString());
    }
    if (this.MediaType != null)
    {
        writer.WriteAttributeString("type", InReplyToElement.NsUri,
                                                    this.MediaType);
    }

    foreach (KeyValuePair<XmlQualifiedName, string> kvp in
                                             this.AttributeExtensions)
    {
        writer.WriteAttributeString(kvp.Key.Name, kvp.Key.Namespace,
                                                   kvp.Value);
    }

    foreach (XElement element in this.ElementExtensions)
    {
        element.WriteTo(writer);
    }
}

ThreadedFeed und ThreadedItem

Im Beispiel werden SyndicationItems mit InReplyTo-Erweiterungen von der ThreadedItem-Klasse modelliert. Entsprechend handelt es sich bei der ThreadedFeed-Klasse um einen SyndicationFeed, dessen Elemente Instanzen von ThreadedItem sind.

Die ThreadedFeed-Klasse erbt von SyndicationFeed und überschreibt OnCreateItem, um ein ThreadedItem zurückzugeben. Außerdem wird eine Methode zum Zugriff auf die Items-Auflistung als ThreadedItems implementiert, wie im folgenden Code dargestellt.

public class ThreadedFeed : SyndicationFeed
{
    public ThreadedFeed()
    {
    }

    public IEnumerable<ThreadedItem> ThreadedItems
    {
        get
        {
            return this.Items.Cast<ThreadedItem>();
        }
    }

    protected override SyndicationItem CreateItem()
    {
        return new ThreadedItem();
    }
}

Die Klasse ThreadedItem erbt von SyndicationItem und legt InReplyToElement als stark typisierte Eigenschaft fest. So kann bequem programmgesteuert auf die InReplyTo-Erweiterungsdaten zugegriffen werden. Außerdem werden TryParseElement und WriteElementExtensions für das Lesen und Schreiben der Erweiterungsdaten implementiert, wie im folgenden Code dargestellt.

public class ThreadedItem : SyndicationItem
{
    private InReplyToElement inReplyTo;
    // Constructors
        public ThreadedItem()
        {
            inReplyTo = new InReplyToElement();
        }

        public ThreadedItem(string title, string content, Uri itemAlternateLink, string id, DateTimeOffset lastUpdatedTime) : base(title, content, itemAlternateLink, id, lastUpdatedTime)
        {
            inReplyTo = new InReplyToElement();
        }

    public InReplyToElement InReplyTo
    {
        get { return this.inReplyTo; }
    }

    protected override bool TryParseElement(
                        System.Xml.XmlReader reader,
                        string version)
    {
        if (version == SyndicationVersions.Atom10 &&
            reader.NamespaceURI == InReplyToElement.NsUri &&
            reader.LocalName == InReplyToElement.ElementName)
        {
            this.inReplyTo = new InReplyToElement();

            this.InReplyTo.ReadXml(reader);

            return true;
        }
        else
        {
            return base.TryParseElement(reader, version);
        }
    }

    protected override void WriteElementExtensions(XmlWriter writer,
                                                 string version)
    {
        if (this.InReplyTo != null &&
                     version == SyndicationVersions.Atom10)
        {
            writer.WriteStartElement(InReplyToElement.ElementName,
                                           InReplyToElement.NsUri);
            this.InReplyTo.WriteXml(writer);
            writer.WriteEndElement();
        }

        base.WriteElementExtensions(writer, version);
    }
}

So können Sie das Beispiel einrichten, erstellen und ausführen

  1. Stellen Sie sicher, dass Sie die Beispiele zum einmaligen Setupverfahren für Windows Communication Foundation ausgeführt haben.

  2. Um die C#- oder Visual Basic .NET-Edition der Projektmappe zu erstellen, befolgen Sie die unter Building the Windows Communication Foundation Samplesaufgeführten Anweisungen.

  3. Wenn Sie das Beispiel in einer Konfiguration mit einem Computer oder über Computer hinweg ausführen möchten, folgen Sie den Anweisungen unter Durchführen der Windows Communication Foundation-Beispiele.