ASP.NET RSS Generator

Here's some code to easily generate RSS feeds from ASP.NET, now finally updated for .NET 2. To use, add a generic handler to your project and change the code so it inherits from RSSGenerator. To add items to the RSS feed override the PopulateFeed method and call AddItem within it, like so.

<%@ WebHandler Language="C#" Class="Handler" %>

using System;
using System.Web;
using RSS;

public class Handler : RSSGenerator
{
  public Handler()
  {
    Title = "Feed title";
    Description = "Feed description";
  }

  protected override void PopulateFeed()
  {
    RSSItem item = AddItem();
    item.Author = "Author";
    item.Date = DateTime.Now;
    item.Description = "sdfsd fsdfsdf sdfsdsdf";
  }
}
Below is the code for the RSSGenerator class and here's an RSS feed for the computer's event log.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Web;
using System.Xml;

namespace RSS
{
  #region RSSItem class
  /// <summary>
  /// An RSS feed item.
  /// </summary>
  public class RSSItem
  {
    private string title;
    /// <summary>
    /// Gets or sets the title for the item.
    /// </summary>
    public string Title
    {
      get { return title; }
      set { title = value; }
    }

    private string description;
    /// <summary>
    /// Gets or sets the description for the item.
    /// </summary>
    public string Description
    {
      get { return description; }
      set { description = value; }
    }

    private DateTime date;
    /// <summary>
    /// Gets or sets the date of the item.
    /// </summary>
    public DateTime Date
    {
      get { return date; }
      set { date = value; }
    }

    private string author;
    /// <summary>
    /// Gets or sets the author of the item.
    /// </summary>
    public string Author
    {
      get { return author; }
      set { author = value; }
    }

    private string link;
    /// <summary>
    /// Gets or sets the URL for the item.
    /// </summary>
    public string Link
    {
      get { return link; }
      set { link = value; }
    }
  }
  #endregion

  #region RSSItemComparer class

  internal class RSSItemComparer : IComparer<RSSItem>
  {
    public int Compare(RSSItem x, RSSItem y)
    {
      return -(x.Date.CompareTo(y.Date));
    }
  }

  #endregion

  #region RSSGenerator class
  /// <summary>
  /// Helper class to generate RSS feeds.
  /// </summary>
  public class RSSGenerator : IHttpHandler
  {
    private List<RSSItem> items = new List<RSSItem>();
    /// <summary>Category for RSS properties</summary>
    protected const string RSSCategory = "RSS";

    public void ProcessRequest(HttpContext context)
    {
      context.Response.ContentType = "text/xml";

      string rss = RSSFeedXml();
      context.Response.Write(rss);
    }

    public bool IsReusable
    {
      get
      {
        return false;
      }
    }

    /// <summary>
    /// Adds an RSS item to the feed.
    /// </summary>
    public RSSItem AddItem()
    {
      RSSItem item = new RSSItem();
      items.Add(item);
      return item;
    }

    /// <summary>
    /// Clears the RSS items from the feed.
    /// </summary>
    public void ClearItems()
    {
      items.Clear();
    }

    #region properties

    private string title;
    /// <summary>
    /// Gets or sets the title of the feed.
    /// </summary>
    [Description("The title of the RSS feed")]
    [Category(RSSCategory)]
    public string Title
    {
      get { return title; }
      set { title = value; }
    }

    private string link;
    /// <summary>
    /// Gets or sets the URL for the website associated with this feed.
    /// </summary>
    [Description("The URL of the homepage for this feed")]
    [Category(RSSCategory)]
    public string Link
    {
      get { return link; }
      set { link = value; }
    }

    private string description;
    /// <summary>
    /// Gets or sets the description for this feed.
    /// </summary>
    [Description("Description of the feed")]
    [Category(RSSCategory)]
    public string Description
    {
      get { return description; }
      set { description = value; }
    }

    #endregion

    /// <summary>
    /// Returns the XML for the RSS feed.
    /// </summary>
    public string RSSFeedXml()
    {
      PopulateFeed();

      StringWriter stringWriter = new StringWriter();
      XmlTextWriter writer = new XmlTextWriter(stringWriter);

      // start document
      writer.WriteStartElement("rss");
      writer.WriteAttributeString("version","2.0");

      // channel
      writer.WriteStartElement("channel");

      writer.WriteElementString("title", title);
      writer.WriteElementString("link", link);
      writer.WriteElementString("description", description);

      items.Sort(new RSSItemComparer());
      foreach(RSSItem item in items)
      {
        writer.WriteStartElement("item");
        writer.WriteElementString("title", item.Title);
        writer.WriteElementString("description", item.Description);
        writer.WriteElementString("pubDate", item.Date.ToString("r"));
        writer.WriteElementString("author", item.Author);
        writer.WriteElementString("link", item.Link);
        writer.WriteEndElement(); // item
      }

      // end channel
      writer.WriteEndElement(); //channel

      // end document
      writer.WriteEndElement(); //rss

      writer.Flush();
      return stringWriter.ToString();
    }

    /// <summary>
    /// Populates the feed. Override to populate the feed
    /// </summary>
    protected virtual void PopulateFeed()
    {
    }
  }
  #endregion
}