Partager via


Exemple d’extensions fortement typées

L’exemple StronglyTypedExtensions utilise la classe SyndicationFeed pour les besoins de l’exemple. Toutefois, les modèles présentés dans cet exemple peuvent être utilisés avec toutes les classes Syndication qui prennent en charge les données d'extension.

Le modèle objet Syndication (SyndicationFeed, SyndicationItem et les classes connexes) prend en charge l’accès peu typé aux données d’extension en utilisant les propriétés AttributeExtensions et ElementExtensions. Cet exemple indique comment fournir un accès fortement typé aux données d'extension en implémentant des classes dérivées personnalisées de SyndicationFeed et SyndicationItem qui rendent disponibles certaines extensions spécifiques de l'application en tant que propriétés fortement typées.

En particulier, cet exemple indique comment implémenter un élément d’extension défini dans la RFC proposée, Atom Threading Extensions. Cet exemple est fourni à titre de démonstration uniquement et n'est pas conçu comme une implémentation complète de la spécification proposée.

Exemple XML

L’exemple de code XML suivant illustre une entrée Atom 1.0 avec un élément d’extension <in-reply-to> supplémentaire.

<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>

L’élément <in-reply-to> spécifie trois attributs obligatoires (ref, type et href) en tenant également compte de la présence d’attributs d’extension et d’éléments d’extension supplémentaires.

Modélisation de l'élément In-Reply-To

Dans cet exemple, l'élément <in-reply-to> est modélisé en tant que CLR qui implémente IXmlSerializable, ce qui active son utilisation avec DataContractSerializer. Il implémente également quelques méthodes et propriétés pour accéder aux données de l'élément, comme le montre l'exemple de code suivant.

[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; }
}

La classe InReplyToElement implémente des propriétés pour l’attribut requis (HRef, MediaType et Source) ainsi que des collections pour stocker AttributeExtensions et ElementExtensions.

La classe InReplyToElement implémente l'interface IXmlSerializable, qui autorise le contrôle direct de la manière dont les instances d'objet sont lues et écrites en XML. La méthode ReadXml lit en premier les valeurs pour les propriétés Ref, HRef, Source et MediaType du XmlReader qui lui est passé. Les attributs inconnus sont stockés dans la collection AttributeExtensions. Lorsque tous les attributs ont été lus, ReadStartElement() est appelé pour faire avancer le lecteur à l'élément suivant. Vu que l'élément modélisé par cette classe ne contient aucun enfant requis, les éléments enfants sont mis en mémoire tampon dans des instances de XElement et stockés dans la collection ElementExtensions, comme illustré dans le code suivant.

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();
    }
}

Dans WriteXml, la méthode InReplyToElement écrit d'abord les valeurs des propriétés Ref, HRef, Source et MediaType en tant qu'attributs XML (WriteXml n'est pas chargé d'écrire l'élément externe réel lui-même, car cela est fait par l'appelant de WriteXml). Elle écrit également le contenu de AttributeExtensions et de ElementExtensions dans le writer, comme illustré dans le code suivant.

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 et ThreadedItem

Dans l'exemple, les SyndicationItems avec les extensions InReplyTo sont modélisés par la classe ThreadedItem. De même, la classe ThreadedFeed est un SyndicationFeed dont les éléments sont tous des instances de ThreadedItem.

La classe ThreadedFeed est héritée de SyndicationFeed et substitue la méthode OnCreateItem pour retourner un ThreadedItem. Elle implémente également une méthode pour accéder à la collection Items en tant que ThreadedItems, comme illustré dans le code suivant.

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

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

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

La classe ThreadedItem est héritée de SyndicationItem et fait de InReplyToElement une propriété fortement typée. Cela fournit accès par programme commode aux données d’extension InReplyTo. Elle implémente également TryParseElement et WriteElementExtensions pour lire et écrire ses données d'extension, comme illustré dans le code suivant.

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);
    }
}

Pour configurer, générer et exécuter l'exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer l’édition C# ou Visual Basic .NET de la solution, conformez-vous aux instructions figurant dans Building the Windows Communication Foundation Samples.

  3. Pour exécuter l’échantillon dans une configuration à un ou plusieurs ordinateurs, conformez-vous aux instructions fournies dans Exécution des échantillons Windows Communication Foundation.