다음을 통해 공유


강력한 형식 확장 샘플

StronglyTypedExtensions 샘플에서는 예를 들기 위해 SyndicationFeed 클래스를 사용하지만 이 샘플에 나온 패턴은 확장 데이터를 지원하는 모든 배포 클래스에서 사용할 수 있습니다.

배포 개체 모델(SyndicationFeed, SyndicationItem 및 관련 클래스)은 AttributeExtensionsElementExtensions 속성을 사용하여 확장명 데이터에 대한 자유로운 형식의 액세스를 지원합니다. 이 샘플에서는 특정 애플리케이션 관련 확장을 강력한 형식의 속성으로 사용할 수 있게 하는 SyndicationFeedSyndicationItem의 사용자 지정 파생 클래스를 구현하여 확장명 데이터에 대해 강력한 형식의 액세스를 제공하는 방법을 보여 줍니다.

한 예로 이 샘플에서는 제안된 Atom Threading Extensions RFC에 정의된 확장명 요소를 구현하는 방법을 보여 줍니다. 이 샘플은 데모용으로만 사용되며 제안된 사양을 전체 구현할 용도는 아닙니다.

샘플 XML

다음 XML 예제에서는 추가 <in-reply-to> 확장명 요소가 포함된 Atom 1.0 항목을 보여 줍니다.

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

<in-reply-to> 요소는 세 가지 필수 특성(ref, type, href)을 지정하며 추가 확장명 특성과 확장명 요소도 허용합니다.

In-Reply-To 요소 모델링

이 샘플에서는 <in-reply-to>와 함께 사용할 수 있도록 IXmlSerializable 요소가 DataContractSerializer을 구현하는 CLR로 모델링됩니다. 또한 다음 샘플 코드에서와 같이 요소의 데이터에 액세스하기 위한 몇몇 메서드와 속성을 구현합니다.

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

InReplyToElement 클래스는 필수 특성의 속성(HRef, MediaTypeSource)뿐 아니라 AttributeExtensionsElementExtensions를 보유하는 컬렉션을 구현합니다.

InReplyToElement 클래스는 XML에서 개체 인스턴스를 읽고 쓰는 방법을 직접 제어하는 IXmlSerializable 인터페이스를 구현합니다. ReadXml 메서드는 먼저 자신에게 전달된 Ref에서 HRef, Source, MediaTypeXmlReader 속성의 값을 읽습니다. 알 수 없는 특성은 모두 AttributeExtensions 컬렉션에 저장됩니다. 모든 특성을 읽으면 ReadStartElement()가 호출되어 판독기가 다음 요소로 진행합니다. 이 클래스에서 모델링된 요소에는 필수 자식이 없으므로 다음 코드에 나온 것처럼 자식 요소는 XElement 인스턴스로 버퍼링되고 ElementExtensions 컬렉션에 저장됩니다.

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

WriteXml에서 InReplyToElement 메서드는 먼저 Ref, HRef, SourceMediaType 속성의 값을 XML 특성으로 씁니다. WriteXmlWriteXml의 호출자가 수행한 실제 외부 요소는 쓰지 않습니다. 다음 코드에 나온 것처럼 이 메서드는 또한 AttributeExtensionsElementExtensions의 내용을 작성기에 씁니다.

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

이 샘플에서는 SyndicationItems 확장을 포함하는 InReplyToThreadedItem 클래스에 의해 모델링됩니다. 마찬가지로 ThreadedFeed 클래스는 항목이 SyndicationFeed의 모든 인스턴스인 ThreadedItem입니다.

ThreadedFeed 클래스는 SyndicationFeed에서 상속되고 OnCreateItem을 재정의하여 ThreadedItem을 반환합니다. 다음 코드에 나온 것처럼 이 클래스는 또한 Items 컬렉션에 액세스하기 위한 메서드를 ThreadedItems로 구현합니다.

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

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

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

ThreadedItem 클래스는 SyndicationItem에서 상속되고 InReplyToElement를 강력한 형식의 속성으로 만듭니다. 이 클래스는 InReplyTo 확장 데이터에 대한 편리한 프로그래밍 액세스를 제공하며, 다음 코드에 나온 것처럼 이 클래스는 또한 확장 데이터를 읽고 쓰기 위한 TryParseElementWriteElementExtensions를 구현합니다.

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

샘플을 설치, 빌드 및 실행하려면

  1. Windows Communication Foundation 샘플의 일회 설치 절차를 수행했는지 확인합니다.

  2. C# 또는 Visual Basic .NET 버전의 솔루션을 빌드하려면 Building the Windows Communication Foundation Samples의 지침을 따릅니다.

  3. 단일 컴퓨터 또는 다중 컴퓨터 구성에서 샘플을 실행하려면 Windows Communication Foundation 샘플 실행의 지침을 따릅니다.