Пример строго типизированных расширений
В примере StronglyTypedExtensions для целей примера используется SyndicationFeed класс. Однако показанные в примере шаблоны можно использовать со всеми классами Syndication, которые поддерживают данные расширения.
Объектная модель синдикации (SyndicationFeed, SyndicationItem и связанные классы) поддерживает слабо типизированный доступ к данным расширений с использованием свойств AttributeExtensions и ElementExtensions. В этом примере показано, как обеспечить строго типизированный доступ к данным расширения путем реализации пользовательских производных классов SyndicationFeed и SyndicationItem обеспечения доступности определенных расширений для конкретного приложения в виде строго типизированных свойств.
Например, здесь показано, как реализовать элемент расширения, определенный в документе RFC "Atom Threading Extensions". Этот образец приводится исключительно с целью демонстрации и не является полной реализацией предлагаемой спецификации.
Образец XML
В следующем примере XML показана запись Atom 1.0 с дополнительным элементом расширения <in-reply-to>
.
<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>
моделируется в среде CLR, которая реализует интерфейс IXmlSerializable, позволяющий использовать его с DataContractSerializer. Он также реализует некоторые методы и свойства для доступа к данным элемента, как показано в следующем примере кода.
[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
, MediaType
и Source
), а также коллекцию для хранения расширений AttributeExtensions и ElementExtensions.
Класс InReplyToElement
реализует интерфейс IXmlSerializable, который позволяет напрямую управлять тем, как экземпляры объектов считываются из XML и записываются в XML. Метод ReadXml
сначала считывает значения свойств Ref
, HRef
, Source
и MediaType
из переданного ему объекта XmlReader. Все неизвестные атрибуты хранятся в коллекции 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
, Source
и MediaType
в виде атрибутов XML (WriteXml
не отвечает за запись самого внешнего элемента, поскольку это делает сторона, вызывающая WriteXml
). Кроме того, он записывает содержимое свойств AttributeExtensions и ElementExtensions в средство записи, как показано в следующем примере кода.
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
с расширениями InReplyTo
моделируются с помощью класса ThreadedItem
. Аналогично класс 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
. Кроме того, он реализует методы TryParseElement
и WriteElementExtensions
для чтения и записи данных расширений, как показано в следующем примере кода.
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);
}
}
Настройка, сборка и выполнение образца
Убедитесь, что вы выполнили процедуру однократной установки для примеров Windows Communication Foundation.
Чтобы создать выпуск решения на языке C# или Visual Basic .NET, следуйте инструкциям в разделе Building the Windows Communication Foundation Samples.
Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе "Примеры Windows Communication Foundation".