Предоставление каналов RSS и Atom
173C# и .NET --- Сетевое программирование --- Предоставление каналов RSS и Atom
Чтение синдицируемых каналов является лишь одним из случаев применения API-интерфейса Syndication. Другой случай связан с предоставлением синдицируемых каналов клиентам RSS и Atom. Для этого в Visual Studio предлагается шаблон Syndication Service Library (Библиотека службы синдикации), который послужит хорошей отправной точкой. Этот шаблон содержит ссылку на библиотеку System.ServiceModel и предусматривает возможность добавления конфигурационного файла приложения для определения конечной точки WCF.
Чтобы предоставить данные для синдицируемого канала, удобно применять ADO.NET Entity Framework. В предлагаемом здесь примере приложении используется база данных Формулы-1, которую вы можете скачать по следующей ссылке - Formula1Model. Сначала в проект добавляется элемент ADO.NET Entity Data Model по имени Formula1. Таблицы Racers, RaceResults, Races и Circuits отображаются на классы сущностей Racer, RaceResult, Race и Circuit, как показано на рисунке:
Затем в шаблоне проекта создается файл IFormula1Feed.cs, в котором размещается контракт службы WCF. В интерфейсе содержится метод CreateFeed(), который возвращает SyndicationFeedFormatter. Поскольку SyndicationFeedFormatter представляет собой абстрактный класс, а в реальности возвращается класс либо типа Atom10FeedFormatter, либо типа Rss20FeedFormatter, эти типы перечисляются в ServiceKnownTypeAttribute, чтобы тип был известен и могла выполняться сериализация.
Атрибут WebGet указывает, что операция может вызываться из простого HTTP-запроса GET, который может применяться для запроса синдицируемых каналов. WebMessageBodyStyle.Bare указывает, что результат (XML из синдицируемого канала) должен отправляться в таком, как он есть виде, без добавления вокруг него элемента-оболочки XML:
using System;
using System.ServiceModel;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;
namespace SyndicationCreator
{
[ServiceContract]
[ServiceKnownType(typeof(Atom10FeedFormatter))]
[ServiceKnownType(typeof(Rss20FeedFormatter))]
public interface IFormula1Feed
{
[OperationContract]
[WebGet(UriTemplate = "*", BodyStyle = WebMessageBodyStyle.Bare)]
SyndicationFeedFormatter CreateFeed();
}
}
Реализация службы выполняется в классе Formula1Feed. Здесь создается элемент SyndicationFeed и производится присваивание различных свойств этого класса, таких как Generator, Language, Title, Categories и Authors. Свойство Items заполняется результатами из LINQ-запроса, который предусматривает извлечение информации о победителях гонок Формулы-1 за определенную дату. В конструкции select этого запроса создается новый анонимный тип, который заполняется несколькими свойствами. Эти свойства затем используется методом Select() для создания объектов SyndicationItem, представляющих победителей.
В SyndicationItem свойству Title присваивается простой текст, содержащий информацию о стране, в которой проводилась гонка. Свойство Content заполняется с помощью LINQ to XML. Классы XElement применяются для создания кода на языке XHTML, который может интерпретироваться браузером. В результате всего этого в содержимом отображается дата, когда проходила гонка, страна, в которой она проходила, и имя участника, который в ней победил.
В зависимости от того, какая строка запроса применяется для запроса синдикации, SyndicationFeed форматируется с помощью либо Atom10FeedFormatter, либо Rss20FeedFormatter:
using System;
using System.Linq;
using System.ServiceModel.Syndication;
using System.ServiceModel.Web;
using System.Xml.Linq;
namespace SyndicationCreator
{
public class Formula1Feed : IFormula1Feed
{
public SyndicationFeedFormatter CreateFeed()
{
DateTime fromDate = DateTime.Today - TimeSpan.FromDays(365);
DateTime toDate = DateTime.Today;
string from = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["from"];
string to = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["to"];
if (from != null && to != null)
{
try
{
fromDate = DateTime.Parse(from);
toDate = DateTime.Parse(to);
}
catch (FormatException)
{
// Сохранение дат по умолчанию
}
}
// Создание нового синдицируемого канала (SyndicationFeed)
var feed = new SyndicationFeed();
feed.Generator = "";
feed.Generator = "C# RSS Sample";
feed.Language = "ru-ru";
feed.LastUpdatedTime = new DateTimeOffset(DateTime.Now);
feed.Title = SyndicationContent.CreatePlaintextContent("Результаты Формулы-1");
feed.Categories.Add(new SyndicationCategory("Формула-1"));
feed.Authors.Add(new SyndicationPerson("alexerohinzzz@gmail.com",
"Christian Nagel", "http://www.professorweb.ru"));
feed.Description = SyndicationContent.CreatePlaintextContent(
"Чемпионы Формулы-1");
using (var data = new Formula1Entities())
{
var races = (from racer in data.Racers
from raceResult in racer.RaceResults
where raceResult.Race.Date > fromDate &&
raceResult.Race.Date < toDate &&
raceResult.Position == 1
orderby raceResult.Race.Date
select new
{
Country = raceResult.Race.Circuit.Country,
Date = raceResult.Race.Date,
Winner = racer.Firstname + " " + racer.Lastname
}).ToArray();
feed.Items = races.Select(race =>
{
return new SyndicationItem
{
Title = SyndicationContent.CreatePlaintextContent(
String.Format("G.P. {0}", race.Country)),
Content = SyndicationContent.CreateXhtmlContent(
new XElement("p",
new XElement("h3", String.Format("{0}, {1}",
race.Country, race.Date.ToShortDateString())),
new XElement("b", String.Format("Winner: {0}", race.Winner))).ToString())
};
});
// Возврат ATOM или RSS в зависимости от строки запроса
// rss -> http://localhost:8732/Design_Time_Addresses/SyndicationService/Feed1/
// atom -> http://localhost:8732/Design_Time_Addresses/SyndicationService/Feed1/?format=atom
string query = WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters["format"];
SyndicationFeedFormatter formatter = null;
if (query == "atom")
{
formatter = new Atom10FeedFormatter(feed);
}
else
{
formatter = new Rss20FeedFormatter(feed);
}
return formatter;
}
}
}
}
При запуске этой службы в среде Visual Studio для ее обслуживания запустится WCF Service Host и в Internet Explorer появится отформатированный результат канала с URL-параметром ?from=1970/1/1&to=1971/1/1.