Предоставление каналов RSS и Atom

173

Чтение синдицируемых каналов является лишь одним из случаев применения 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.

Пройди тесты
Лучший чат для C# программистов