With this implementation you can re-use parts of your content Controller (the database query for example) and render it on a RSS and/or ATOM feed.
Let's say you have an action like this (extracted from DotNetBurner’s source):
[AcceptVerbs (HttpVerbs.Get | HttpVerbs.Head), CompressFilter] public ActionResult Upcoming (int? page, string sort) { var viewData = GetStoryListViewData<StoryListByCategoryData> (page); int sortDays = GetSortDays (sort); viewData.Stories = Repository.GetUpcomingStories (CurrentUserId, sortDays, SiteSettings.Settings.UpcomingMaxDays, CalculateStartIndex (page), StorySettings.Settings.StoryPerPage); viewData.Category = Strings.Upcoming; viewData.CategoryDisplayName = Strings.Upcoming; viewData.SortDays = sortDays; return View ("Upcoming", viewData); }
This action returns the upcoming stories. To turn it on a feed, i have just implement this new action:
[AcceptVerbs (HttpVerbs.Get | HttpVerbs.Head), CompressFilter, OutputCache (CacheProfile = "Feeds")] public ActionResult UpcomingFeed(string sort, string feedType, int? feedCount) { return Content(Feed("Upcoming", null, sort, feedType, feedCount), "application/xml", Encoding.UTF8); } string Feed(string feed, int? id, string sort, string feedType, int? feedCount) { int sortDays = GetSortDays(sort); if (!id.HasValue) id = 0; if (!feedCount.HasValue || feedCount == 0) feedCount = StorySettings.Settings.StoryPerFeed; if (feedCount > 200) feedCount = 200; IEnumerable<Story> stories = null; var categories = Repository.GetCategories(); stories = Repository.GetUpcomingStories (CurrentUserId, sortDays, SiteSettings.Settings.UpcomingMaxDays, 1, (int)feedCount); FeedType type = ("rss".Equals(feedType, StringComparison.InvariantCultureIgnoreCase)) ? FeedType.RSS20 : FeedType.ATOM10; using (var memoryStream = new MemoryStream ()) { FeedHelper.GenerateFeed (stories, categories, memoryStream, type, Url); return Encoding.UTF8.GetString (memoryStream.ToArray ()); } }
The Feed method is simplified here for the sake of the example, but it can take a “feed” parameter to return different feeds (for categories, tags, etc.). To generate the feed, i have implemented a FeedHelper class (you probably will have to tune it for your needs):
public enum FeedType { RSS20, ATOM10 } public static class FeedHelper { public static void GenerateFeed(IEnumerable<Story> stories, IEnumerable<Category> categories, Stream output, FeedType type, UrlHelper urlHelper) { // Create an XmlWriter to write the feed into it using (XmlWriter writer = XmlWriter.Create(output)) { // Set the feed properties SyndicationFeed feed = new SyndicationFeed (SiteSettings.Settings.Name, SiteSettings.Settings.Title, new Uri(SiteSettings.Settings.Address)); feed.LastUpdatedTime = DateTime.UtcNow; feed.Language = SiteSettings.Settings.Language; feed.Authors.Add(new SyndicationPerson(SiteSettings.Settings.AdminMail, SiteSettings.Settings.Name, SiteSettings.Settings.Address)); // Add categories foreach (var category in categories) foreach(var subcat in category.SubCategories) feed.Categories.Add(new SyndicationCategory(subcat.Name)); // Set generator feed.Generator = SiteSettings.Settings.Title; // Set language feed.Language = SiteSettings.Settings.Language; string siteUrl = SiteSettings.Settings.Address; // Add post items List<SyndicationItem> items = new List<syndicationitem>(); foreach (var story in stories) { string url = String.Concat(siteUrl, urlHelper.RouteUrl("Detail", new RouteValueDictionary { {"id", story.StoryId}, {"title", story.Title.ConvertToUrlPath()} })); string voteButton = "{0}<br>".FormatWith(SwissKnife.GetVoteButtonFor(story.Url)); TextSyndicationContent content = SyndicationContent.CreateHtmlContent(String.Concat("<div><p>", story.Description, "</p>", voteButton, "</div>")); SyndicationItem item = new SyndicationItem(story.Title, content, new Uri(url), url, new DateTimeOffset(story.PostedOn)); item.PublishDate = story.PostedOn; item.Categories.Add(new SyndicationCategory(story.CategoryName)); items.Add(item); } feed.Items = items; // Write the feed to output if (type == FeedType.RSS20) { Rss20FeedFormatter rssFormatter = new Rss20FeedFormatter(feed); rssFormatter.WriteTo(writer); } else { Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(feed); atomFormatter.WriteTo(writer); } writer.Flush(); } } }
This uses System.ServiceModel.Syndication to create a feed from the same database query that is used to render the site content. With this, i have enabled DotNetBurner to have feeds in less than 4 hours.
Drop me a comment if you implement this on a similar time :)