Compartilhar via


Exemplo de extensões fortemente tipadas

O exemplo StronglyTypedExtensions usa a classe SyndicationFeed para fins demonstrativos. No entanto, os padrões expostos neste exemplo podem ser usados com todas as classes da sindicalização que dão suporte a dados de extensão.

O modelo de objeto de sindicalização (SyndicationFeed, SyndicationItem e as classes relacionadas) dá suporte ao acesso fracamente tipado aos dados de extensão usando as propriedades AttributeExtensions e ElementExtensions. Este exemplo mostra como fornecer acesso fortemente tipado aos dados de extensão implementando classes derivadas personalizadas de SyndicationFeed e SyndicationItem que disponibilizam determinadas extensões específicas do aplicativo como propriedades fortemente tipadas.

Como uma demonstração, este exemplo mostra como implementar um elemento de extensão definido no RFC de extensões de threading Atom propostas. Isso é somente para fins explicativos e o exemplo não se destina a ser uma implementação completa da especificação proposta.

XML de exemplo

O exemplo de XML a seguir mostra uma entrada de Atom 1.0 com um elemento de extensão <in-reply-to> adicional.

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

O elemento <in-reply-to> especifica três atributos necessários (ref, type e href), além de permitir a presença de elementos de extensão e atributos de extensão adicionais.

Modelagem do elemento In-Reply-To

Neste exemplo, o elemento <in-reply-to> é modelado como o CLR que implementa IXmlSerializable, o que permite seu uso com DataContractSerializer. Ele também implementa alguns métodos e propriedades para acessar os dados do elemento, conforme mostrado no código de exemplo a seguir.

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

A classe InReplyToElement implementa propriedades para o atributo necessário (HRef, MediaType e Source), bem como coleções para manter AttributeExtensions e ElementExtensions.

A classe InReplyToElement implementa a interface IXmlSerializable, que permite controle direto sobre como as instâncias de objeto são lidas e gravadas em XML. O método ReadXml primeiro lê os valores das propriedades Ref, HRef, Source e MediaType do XmlReader transmitido a ele. Todos os atributos desconhecidos são armazenados na coleção AttributeExtensions. Quando todos os atributos forem lidos, ReadStartElement() será chamado para levar o leitor ao próximo elemento. Como o elemento modelado por essa classe não tem filhos necessários, os elementos filho são armazenados em buffer em instâncias de XElement e armazenados na coleção ElementExtensions, conforme mostrado no código a seguir.

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

Em WriteXml, o método InReplyToElement primeiro grava os valores das propriedades Ref, HRef, Source e MediaType como atributos XML (WriteXml não é responsável por gravar o elemento externo real em si, como feito pelo chamador de WriteXml). Ele também grava o conteúdo de AttributeExtensions e ElementExtensions no gravador, conforme mostrado no código a seguir.

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

No exemplo, SyndicationItems com extensões InReplyTo são modelados pela classe ThreadedItem. Da mesma forma, a classe ThreadedFeed é um SyndicationFeed cujos itens são todos instâncias de ThreadedItem.

A classe ThreadedFeed herda de SyndicationFeed e substitui OnCreateItem para retornar um ThreadedItem. Ela também implementa um método para acessar a coleção Items como ThreadedItems, conforme mostrado no código a seguir.

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

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

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

A classe ThreadedItem herda de SyndicationItem e faz de InReplyToElement uma propriedade fortemente tipada. Isso fornece acesso programático conveniente aos dados de extensão de InReplyTo. Também implementa TryParseElement e WriteElementExtensions para ler e gravar os dados de extensão, conforme mostrado no código a seguir.

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

Para configurar, compilar, e executar o exemplo

  1. Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.

  2. Para compilar a edição .NET do C# ou do Visual Basic da solução, siga as instruções contidas em Como Compilar as Amostras do Windows Communication Foundation.

  3. Para executar a amostra em uma configuração de computador único ou entre computadores, siga as instruções contidas em Como executar as amostras do Windows Communication Foundation.