Create RSS and Atom feeds using custom ASP.Net MVC Action Results and the Microsoft Syndication classes

There are many ways to create RSS and Atom feeds, in this post I’m going to show one way of creating a re-usable solution that utilises the Syndication classes from the System.ServiceModel.Web assembly.

To make good use of the ASP.Net MVC framework I am going to show a nice way of creating custom action result methods to return the feeds without the need for creating any views.

As this is quite a detailed overview with a fair amount of code – you can download a working solution here.

The first thing to do is create the custom ActionResult methods to return our feeds.

You will need to add a project reference to the System.ServiceModel.Web assembly.

RssActionResult

 

  1. using System.ServiceModel.Syndication;
  2. using System.Web.Mvc;
  3. using System.Xml;
  4. namespace SyndicationTest
  5. {
  6.     /// <summary>
  7.     /// Returns an RSS feed to the resonse stream.
  8.     /// </summary>
  9.     public class RssActionResult : ActionResult
  10.     {
  11.         SyndicationFeed feed;
  12.         /// <summary>
  13.         /// Default constructor.
  14.         /// </summary>
  15.         public RssActionResult() { }
  16.         /// <summary>
  17.         /// Constructor to set up the action result feed.
  18.         /// </summary>
  19.         /// <param name="feed">Accepts a <see cref="SyndicationFeed"/>.</param>
  20.         public RssActionResult(SyndicationFeed feed)
  21.         {
  22.             this.feed = feed;
  23.         }
  24.         /// <summary>
  25.         /// Executes the call to the ActionResult method and returns the created feed to the output response.
  26.         /// </summary>
  27.         /// <param name="context">Accepts the current <see cref="ControllerContext"/>.</param>
  28.         public override void ExecuteResult(ControllerContext context)
  29.         {           
  30.             context.HttpContext.Response.ContentType = "application/rss+xml";
  31.             Rss20FeedFormatter formatter = new Rss20FeedFormatter(this.feed);
  32.             using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
  33.             {
  34.                 formatter.WriteTo(writer);
  35.             }
  36.         }
  37.     }
  38. }

 

AtomActionResult

  1. using System.ServiceModel.Syndication;
  2. using System.Web.Mvc;
  3. using System.Xml;
  4. namespace SyndicationTest
  5. {
  6.     /// <summary>
  7.     /// Returns an Atom feed to the resonse stream.
  8.     /// </summary>
  9.     public class AtomActionResult : ActionResult
  10.     {
  11.         SyndicationFeed feed;
  12.         /// <summary>
  13.         /// Default constructor.
  14.         /// </summary>
  15.         public AtomActionResult() { }
  16.         /// <summary>
  17.         /// Constructor to set up the action result feed.
  18.         /// </summary>
  19.         /// <param name="feed">Accepts a <see cref="SyndicationFeed"/>.</param>
  20.         public AtomActionResult(SyndicationFeed feed)
  21.         {
  22.             this.feed = feed;
  23.         }
  24.         /// <summary>
  25.         /// Executes the call to the ActionResult method and returns the created feed to the output response.
  26.         /// </summary>
  27.         /// <param name="context">Accepts the current <see cref="ControllerContext"/>.</param>
  28.         public override void ExecuteResult(ControllerContext context)
  29.         {
  30.             context.HttpContext.Response.ContentType = "application/atom+xml";
  31.             Atom10FeedFormatter formatter = new Atom10FeedFormatter(this.feed);
  32.             using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
  33.             {
  34.                 formatter.WriteTo(writer);
  35.             }
  36.         }
  37.     }
  38. }

Now that the action result methods have been created, they can be called from your views using the same syntax as any other action result method.

Now assuming that you will be returning a list of items from your repository that you will want mapped into a syndication feed, we need to create a class that will hold property mappings from your list of custom types to the internal properties of the syndication helper class that we will be creating shortly.

This mapping class will be used simply for storing delegate properties used for mappings, this will allow for easy, strongly typed mapping.

SyndicationFeedItemMapper

  1. using System;
  2. namespace SyndicationTest
  3. {
  4.     public class SyndicationFeedItemMapper<TFeedItem> where TFeedItem : class
  5.     {
  6.         Func<TFeedItem, string> title;
  7.         Func<TFeedItem, string> content;
  8.         Func<TFeedItem, string> controller;
  9.         Func<TFeedItem, string> action;
  10.         Func<TFeedItem, string> id;
  11.         Func<TFeedItem, DateTimeOffset> datePublished;
  12.         string controllerString;
  13.         string actionString;
  14.         public SyndicationFeedItemMapper
  15.             (
  16.                 Func<TFeedItem, string> title,
  17.                 Func<TFeedItem, string> content,
  18.                 string controller,
  19.                 string action,
  20.                 Func<TFeedItem, string> id,
  21.                 Func<TFeedItem, DateTimeOffset> datePublished
  22.             ) : this(title, content, id, datePublished)
  23.         {
  24.             this.controllerString = controller;
  25.             this.actionString = action;
  26.         }
  27.         public SyndicationFeedItemMapper
  28.             (
  29.                 Func<TFeedItem, string> title,
  30.                 Func<TFeedItem, string> content,
  31.                 Func<TFeedItem, string> controller,
  32.                 Func<TFeedItem, string> action,
  33.                 Func<TFeedItem, string> id,
  34.                 Func<TFeedItem, DateTimeOffset> datePublished
  35.             )
  36.             : this(title, content, id, datePublished)
  37.         {
  38.             this.controller = controller;
  39.             this.action = action;
  40.         }
  41.         protected SyndicationFeedItemMapper
  42.             (
  43.                 Func<TFeedItem, string> title,
  44.                 Func<TFeedItem, string> content,
  45.                 Func<TFeedItem, string> id,
  46.                 Func<TFeedItem, DateTimeOffset> datePublished
  47.             )
  48.         {
  49.             this.title = title;
  50.             this.content = content;
  51.             this.id = id;
  52.             this.datePublished = datePublished;
  53.         }
  54.         public Func<TFeedItem, string> Title
  55.         {
  56.             get
  57.             {
  58.                 return this.title;
  59.             }
  60.         }
  61.         public Func<TFeedItem, string> Content
  62.         {
  63.             get
  64.             {
  65.                 return this.content;
  66.             }
  67.         }
  68.         public Func<TFeedItem, string> Controller
  69.         {
  70.             get
  71.             {
  72.                 return this.controller;
  73.             }
  74.         }
  75.         public Func<TFeedItem, string> Action
  76.         {
  77.             get
  78.             {
  79.                 return this.action;
  80.             }
  81.         }
  82.         public Func<TFeedItem, string> Id
  83.         {
  84.             get
  85.             {
  86.                 return this.id;
  87.             }
  88.         }
  89.         public Func<TFeedItem, DateTimeOffset> DatePublished
  90.         {
  91.             get
  92.             {
  93.                 return this.datePublished;
  94.             }
  95.         }
  96.         public string ControllerString
  97.         {
  98.             get
  99.             {
  100.                 return this.controllerString;
  101.             }
  102.         }
  103.         public string ActionString
  104.         {
  105.             get
  106.             {
  107.                 return this.actionString;
  108.             }
  109.         }
  110.         public Func<TFeedItem, string> AuthorName { get; set; }
  111.         public Func<TFeedItem, string> AuthorEmail { get; set; }
  112.         public Func<TFeedItem, string> AuthorUrl { get; set; }
  113.   
  114.     }
  115. }

You will notice that there is also support for allowing the controller and action method to be stored as a string rather than a delegate, this is because you may not want to hold this information in your repository.

To show you an example of how this mapping class is used.

Lets say the items come back from your repository into a list of MyFeedItem:

MyFeedItem

  1. using System;
  2. namespace SyndicationTest.Models
  3. {
  4.     public class MyFeedItem
  5.     {
  6.         public int Id { get; set; }
  7.         public string Title { get; set; }
  8.         public string Description { get; set; }
  9.         public DateTime DateAdded { get; set; }
  10.         public string CreatedBy { get; set; }
  11.     }
  12. }

These properties can be used to set up the feed mapper for example:

  1. SyndicationFeedItemMapper<MyFeedItem> mapper = new SyndicationFeedItemMapper<MyFeedItem>
  2.     (
  3.         f => f.Title,
  4.         f => f.Description,
  5.         "Home",
  6.         "Articles",
  7.         f => f.Id.ToString(),
  8.         f => f.DateAdded
  9.     );

Now that the items have been covered, you can create an options class to store information such as the feed title, description and url, there are also numerous optional properties you can set such as feed id, copyright statement, last updated date and language.

SyndicationFeedOptions

  1. using System;
  2. namespace SyndicationTest
  3. {
  4.     public class SyndicationFeedOptions
  5.     {
  6.         string title;
  7.         string description;
  8.         string url;
  9.         public SyndicationFeedOptions(string title, string description, string url)
  10.         {
  11.             this.title = title;
  12.             this.description = description;
  13.             this.url = url;
  14.         }
  15.    
  16.         public string Title
  17.         {
  18.             get
  19.             {
  20.                 return this.title;
  21.             }
  22.         }
  23.         public string Description
  24.         {
  25.             get
  26.             {
  27.                 return this.description;
  28.             }
  29.         }
  30.         public string Url
  31.         {
  32.             get
  33.             {
  34.                 return this.url;
  35.             }
  36.         }
  37.         public string FeedId { get; set; }
  38.         public DateTimeOffset LastUpdated { get; set; }
  39.         public string Copyright { get; set; }
  40.         public string Language { get; set; }
  41.     }
  42. }

You can populate the SyndicationFeedOptions class like the following:

  1. SyndicationFeedOptions options = new SyndicationFeedOptions
  2.                 (
  3.                     "My Feed Title",
  4.                     "My Feed Description",
  5.                     "http://mytesturl.com"
  6.                 );

Now that all the support classes have been built, it is time to build the main SyndicationFeedHelper class that will take in all the information and return a SyndicationFeed for converting into RSS or Atom.

The are only two public methods in this class, the constructor which sets up the feed options and stores the current controller context, and the GetFeed() method which calls the necessary private methods to create and then return the feed.

The most interesting private methods are probably the CreateSyndicationItem and the GetUrl.

The CreateSyndicationItem accepts an instance of the MyFeedItem class from the passed in list and invokes the delegates stored in the mapper against it. This then extracts the instance values and creates a SyndicationItem.

The GetUrl method accepts an instance of the MyFeedItem class from the passed in list and invokes the necessary delegates in the mapper against it to create valid MVC style urls for each item.

SyndicationFeedHelper

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.ServiceModel.Syndication;
  5. using System.Web.Mvc;
  6. using System.Web.Routing;
  7. namespace SyndicationTest
  8. {
  9.     /// <summary>
  10.     /// Class to create an syndication feed.
  11.     /// </summary>
  12.     /// <typeparam name="TFeedItem">Accepts the custom feed item type.</typeparam>
  13.     public class SyndicationFeedHelper<TFeedItem> where TFeedItem : class
  14.     {
  15.         const string Id = "id";
  16.         ControllerContext context;
  17.         IList<TFeedItem> feedItems;
  18.         SyndicationFeedItemMapper<TFeedItem> feedItemMapper;
  19.         SyndicationFeedOptions feedOptions;
  20.         SyndicationFeed syndicationFeed;
  21.         IList<SyndicationItem> syndicationItems;
  22.         ControllerContext Context
  23.         {
  24.             get
  25.             {
  26.                 return this.context;
  27.             }
  28.         }
  29.         IList<TFeedItem> FeedItems
  30.         {
  31.             get
  32.             {
  33.                 return this.feedItems;
  34.             }
  35.         }
  36.         SyndicationFeedItemMapper<TFeedItem> FeedItemMapper
  37.         {
  38.             get
  39.             {
  40.                 return this.feedItemMapper;
  41.             }
  42.         }
  43.         SyndicationFeedOptions SyndicationFeedOptions
  44.         {
  45.             get
  46.             {
  47.                 return this.feedOptions;
  48.             }
  49.         }
  50.         /// <summary>
  51.         /// Constructs the SyndicationFeedHelper.
  52.         /// </summary>
  53.         /// <param name="context">Accepts the current controller context.</param>
  54.         /// <param name="feedItems">Accepts a list of feed items.</param>
  55.         /// <param name="syndicationFeedItemMapper">Accepts a <see cref="SyndicationFeedItemMapper"/>.</param>
  56.         /// <param name="syndicationFeedOptions">Accepts a <see cref="SyndicationFeedOptions"/>.</param>
  57.         public SyndicationFeedHelper
  58.             (
  59.                 ControllerContext context,
  60.                 IList<TFeedItem> feedItems,
  61.                 SyndicationFeedItemMapper<TFeedItem> syndicationFeedItemMapper,
  62.                 SyndicationFeedOptions syndicationFeedOptions
  63.             )
  64.         {
  65.             this.context = context;
  66.             this.feedItems = feedItems;
  67.             this.feedItemMapper = syndicationFeedItemMapper;
  68.             this.feedOptions = syndicationFeedOptions;
  69.             syndicationItems = new List<SyndicationItem>();
  70.             SetUpFeedOptions();
  71.         }
  72.         /// <summary>
  73.         /// Creates and returns a <see cref="SyndicationFeed"/>.
  74.         /// </summary>
  75.         /// <returns><see cref="SyndicationFeed"/></returns>
  76.         public SyndicationFeed GetFeed()
  77.         {
  78.             feedItems.ToList().ForEach(feedItem =>
  79.             {
  80.                 SyndicationItem syndicationItem = CreateSyndicationItem(feedItem);
  81.                 AddItemAuthor(feedItem, syndicationItem);
  82.                 AddPublishDate(feedItem, syndicationItem);
  83.                 syndicationItems.Add(syndicationItem);
  84.             });
  85.             syndicationFeed.Items = syndicationItems;
  86.             return syndicationFeed;
  87.         }
  88.         private void SetUpFeedOptions()
  89.         {
  90.             syndicationFeed = new SyndicationFeed
  91.                 (
  92.                     this.SyndicationFeedOptions.Title,
  93.                     this.SyndicationFeedOptions.Description,
  94.                     new Uri(this.SyndicationFeedOptions.Url)
  95.                 );
  96.             AddFeedId();
  97.             AddCopyrightStatement();
  98.             AddLanguageIsoCode();
  99.             AddLastUpdateDateTime();
  100.         }
  101.         private void AddLastUpdateDateTime()
  102.         {
  103.             if (this.SyndicationFeedOptions.LastUpdated != default(DateTimeOffset))
  104.             {
  105.                 syndicationFeed.LastUpdatedTime = this.SyndicationFeedOptions.LastUpdated;
  106.             }
  107.         }
  108.         private void AddLanguageIsoCode()
  109.         {
  110.             if (!string.IsNullOrEmpty(this.SyndicationFeedOptions.Language))
  111.             {
  112.                 syndicationFeed.Language = this.SyndicationFeedOptions.Language;
  113.             }
  114.         }
  115.         private void AddCopyrightStatement()
  116.         {
  117.             if (!string.IsNullOrEmpty(this.SyndicationFeedOptions.Copyright))
  118.             {
  119.                 syndicationFeed.Copyright = new TextSyndicationContent(this.SyndicationFeedOptions.Copyright);
  120.             }
  121.         }
  122.         private void AddFeedId()
  123.         {
  124.             if (!string.IsNullOrEmpty(this.SyndicationFeedOptions.FeedId))
  125.             {
  126.                 syndicationFeed.Id = this.SyndicationFeedOptions.FeedId;
  127.             }
  128.         }
  129.         private void AddPublishDate(TFeedItem feedItem, SyndicationItem syndicationItem)
  130.         {
  131.             var publishDate = this.FeedItemMapper.DatePublished.Invoke(feedItem);
  132.             syndicationItem.PublishDate = publishDate;
  133.         }
  134.         private void AddItemAuthor(TFeedItem feedItem, SyndicationItem syndicationItem)
  135.         {
  136.             var authorEmail = this.FeedItemMapper.AuthorEmail == null ? string.Empty : this.FeedItemMapper.AuthorEmail.Invoke(feedItem);
  137.             var authorName = this.FeedItemMapper.AuthorName == null ? string.Empty : this.FeedItemMapper.AuthorName.Invoke(feedItem);
  138.             var authorUrl = this.FeedItemMapper.AuthorUrl == null ? string.Empty : this.FeedItemMapper.AuthorUrl.Invoke(feedItem);
  139.             SyndicationPerson syndicationPerson = new SyndicationPerson();
  140.             if (string.IsNullOrEmpty(authorName))
  141.             {
  142.                 syndicationPerson.Name = authorName;
  143.             }
  144.             if (string.IsNullOrEmpty(authorEmail))
  145.             {
  146.                 syndicationPerson.Email = authorEmail;
  147.             }
  148.             if (string.IsNullOrEmpty(authorUrl))
  149.             {
  150.                 syndicationPerson.Uri = authorUrl;
  151.             }
  152.             if (!string.IsNullOrEmpty(syndicationPerson.Name) || !string.IsNullOrEmpty(syndicationPerson.Email)
  153.                 || !string.IsNullOrEmpty(syndicationPerson.Uri))
  154.             {
  155.                 syndicationItem.Authors.Add(syndicationPerson);
  156.             }
  157.         }
  158.         private SyndicationItem CreateSyndicationItem(TFeedItem feedItem)
  159.         {
  160.             SyndicationItem syndicationItem = new SyndicationItem
  161.                 (
  162.                     this.FeedItemMapper.Title.Invoke(feedItem),
  163.                     this.FeedItemMapper.Content.Invoke(feedItem),
  164.                     this.GetUrl(feedItem)
  165.                 );
  166.             return syndicationItem;
  167.         }
  168.         private Uri GetUrl(TFeedItem feedItem)
  169.         {
  170.             UrlHelper urlHelper = new UrlHelper(this.Context.RequestContext);
  171.             var routeValues = new RouteValueDictionary();
  172.             routeValues.Add(Id, this.FeedItemMapper.Id.Invoke(feedItem));
  173.             var action = this.FeedItemMapper.Action == null ? this.FeedItemMapper.ActionString
  174.                 : this.FeedItemMapper.Action.Invoke(feedItem);
  175.             var controller = this.FeedItemMapper.Controller == null ? this.FeedItemMapper.ControllerString
  176.                 : this.FeedItemMapper.Controller.Invoke(feedItem);
  177.             var url = urlHelper.Action
  178.                 (
  179.                     action,
  180.                     controller,
  181.                     routeValues
  182.                 );
  183.             Uri uriPath = this.Context.RequestContext.HttpContext.Request.Url;
  184.             var urlString = string.Format("{0}://{1}{2}",uriPath.Scheme, uriPath.Authority, url);
  185.             return new Uri(urlString);
  186.         }
  187.     }
  188. }

At this point you have a fully functional, re-usable solution for displaying RSS and Atom feeds on your site.

You can now create some action results on your controller to return your feeds.

Here is an example of the Home controller on the downloadable example:

  1. using System.Collections.Generic;
  2. using System.Web.Mvc;
  3. using SyndicationTest.Models;
  4. using SyndicationTest.Repositories;
  5. namespace SyndicationTest.Controllers
  6. {
  7.     [HandleError]
  8.     public class HomeController : Controller
  9.     {
  10.         private FakeRepository fakeRepository = new FakeRepository();
  11.         /// <summary>
  12.         /// Index Action Result.
  13.         /// </summary>
  14.         /// <returns></returns>
  15.         public ActionResult Index()
  16.         {
  17.             return View();
  18.         }
  19.         /// <summary>
  20.         /// Returns an RSS Feed
  21.         /// </summary>
  22.         /// <param name="id">Accepts an int id.</param>
  23.         /// <returns></returns>
  24.         public RssActionResult GetRssFeed(int id)
  25.         {
  26.             SyndicationFeedItemMapper<MyFeedItem> mapper = SetUpFeedMapper();
  27.             SyndicationFeedOptions options = SetUpFeedOptions();
  28.             IList<MyFeedItem> feedItems = this.fakeRepository.GetLatestFeedItems(id);
  29.             SyndicationFeedHelper<MyFeedItem> feedHelper = SetUpFeedHelper(mapper, options, feedItems);
  30.             return new RssActionResult(feedHelper.GetFeed());
  31.         }
  32.         /// <summary>
  33.         /// Returns an Atom feed.
  34.         /// </summary>
  35.         /// <param name="id">Accepts an int id.</param>
  36.         /// <returns></returns>
  37.         public AtomActionResult GetAtomFeed(int id)
  38.         {
  39.             SyndicationFeedItemMapper<MyFeedItem> mapper = SetUpFeedMapper();
  40.             SyndicationFeedOptions options = SetUpFeedOptions();
  41.             IList<MyFeedItem> feedItems = this.fakeRepository.GetLatestFeedItems(id);
  42.             SyndicationFeedHelper<MyFeedItem> feedHelper = SetUpFeedHelper(mapper, options, feedItems);
  43.             return new AtomActionResult(feedHelper.GetFeed());
  44.         }
  45.         private SyndicationFeedHelper<MyFeedItem> SetUpFeedHelper(SyndicationFeedItemMapper<MyFeedItem> mapper,
  46.             SyndicationFeedOptions options, IList<MyFeedItem> feedItems)
  47.         {
  48.             SyndicationFeedHelper<MyFeedItem> feedHelper = new SyndicationFeedHelper<MyFeedItem>
  49.                 (
  50.                     this.ControllerContext,
  51.                     feedItems,
  52.                     mapper,
  53.                     options
  54.                 );
  55.             return feedHelper;
  56.         }
  57.         private static SyndicationFeedOptions SetUpFeedOptions()
  58.         {
  59.             SyndicationFeedOptions options = new SyndicationFeedOptions
  60.                 (
  61.                     "My Feed Title",
  62.                     "My Feed Description",
  63.                     "http://mytesturl.com"
  64.                 );
  65.             return options;
  66.         }
  67.         private static SyndicationFeedItemMapper<MyFeedItem> SetUpFeedMapper()
  68.         {
  69.             SyndicationFeedItemMapper<MyFeedItem> mapper = new SyndicationFeedItemMapper<MyFeedItem>
  70.                 (
  71.                     f => f.Title,
  72.                     f => f.Description,
  73.                     "Home",
  74.                     "Articles",
  75.                     f => f.Id.ToString(),
  76.                     f => f.DateAdded
  77.                 );
  78.             return mapper;
  79.         }
  80.     }
  81. }

You can call these action results in the same way as another type of action result for example:

  1. <%= this.Html.ActionLink("RSS Feed", "GetRssFeed", new { id = 123 })%>
  2.     <br />
  3.     <%= this.Html.ActionLink("Atom Feed", "GetAtomFeed", new { id = 456 })%>

I hope this overview is helpful.

Kind Regards,

Sean McAlinden.

www.asp-net-mvc.com

10 Comments

  • Thanks for your info on Syndication.

    One issue is that every time the feed is refreshed, a copy of the whole feed is appended with new times. This happens even if DateAdded is set to a constant.

    Any idea how to make it so it doesn't add duplicates every time its refreshed?

  • Hi Nolan,

    It sounds like the source data for your feed could be the problem, especially if the same data is coming back with new dates, if you have access to the data store/repository I would take a look at the query getting the results.
    I don't think the above code could cause the issue you're having as it should only display whatever is passed in as the feed list.
    If you work out that it's definitely not the source data, let me know and I'll see if I can reproduce (it's been a while since I've looked at the code).
    Hope you get it sorted.

    Cheers,
    Sean.

  • Thanks for the promt reply.

    I'm just basically using your FakeRepository except I have added
    DateAdded = DateTime.Parse("2008-05-01 7:34:42Z")

    It works OK when you just refresh the page but if you subscribe to it in IE and keep refreshing you get a long list of repeats. This doesn't happen for other feeds.

    I note that the time visible in the feed item keeps being updated rather than staying at the DateTime.Parse("2008-05-01 7:34:42Z"). Sorry, I don't know how this mappes to AtomPub XML elements.



    Any help would be much appreciated.

    Thanks Sean.

  • Hiya, I'll build the code and take a look - must be a bug then - I'll post a comment as soon as I've worked it out.

    Cheers,
    Sean.

  • It seems to work when these lines are added to SyndicationFeedHelper AddPublishDate

    syndicationItem.LastUpdatedTime = publishDate;
    syndicationItem.Id = this.FeedItemMapper.Id.Invoke(feedItem);

    But comments and correct refactor would be appreciated.

    Thanks

  • Hi Nolan,

    It is very odd, I can't reproduce the list of repeating items but I can see that the date is being ignored on the atom feed.

    It looks like Atom display dates map to the LastUpdatedDate rather than the publish date - a fix for this would be to add the following code syndicationItem.LastUpdatedTime = syndicationItem.PublishDate; within the GetFeed method on the SydicationFeedHelper class after the AddPublishDate(feedItem, syndicationItem); line.
    Hope this solves it.
    Cheers,
    Sean.

  • I moved all below to what I believe is the correct location SyndicationFeedHelper GetFeed

    syndicationItem.LastUpdatedTime = syndicationItem.PublishDate;
    syndicationItem.Id = syndicationItem.Id;
    syndicationItem.Content = new TextSyndicationContent(this.FeedItemMapper.Content.Invoke(feedItem), TextSyndicationContentKind.XHtml);

    The first two make it so a refresh will not duplicate items and that the date diplayed in the feed will be correct.

    The last line sets the Content Type to XHtml

  • Great project! I upgraded it to VS2010 with a couple of issues.

    1. Upgrade by starting the solution file resulted in a failed conversion. Upgrade by starting the project file converted successfully.

    2. Targeting the .NET 4.0 Framework breaks the project until you add an assembly reference to the System.ServiceModel dll.

    I also like the fake respository concept for a testbed project like this. Very nice job, cutting right to the chase with a clean MVC solution.

    Thanks!

  • I create a leave a response whenever I especially enjoy a article on a website or I have something to valuable to contribute to the conversation.

    It's triggered by the sincerness communicated in the article I looked at. And on this post Create RSS and Atom feeds using custom ASP.Net MVC Action Results and the Microsoft Syndication classes - Sean McAlinden's Blog.
    I was actually excited enough to drop a thought ;-) I actually
    do have 2 questions for you if you usually do not mind.
    Could it be just me or does it look as if like some of the comments come across as if they are written
    by brain dead individuals? :-P And, if you are writing at additional online social
    sites, I'd like to keep up with you. Would you make a list all of all your community pages like your twitter feed, Facebook page or linkedin profile?

  • Hi there Dear, are you actually visiting this site regularly, if
    so after that you will without doubt get good knowledge.

Comments have been disabled for this content.