Für meine neue Frontseite von marcduerst.com habe ich etwas mit den .Net System Libraries für Syndication gearbeitet. Dabei habe ich einerseits gesehen, wie einfach das Syndicytion-API sowie der XML-API sein kann. In diesem Artikel sind einige Beispiele zu finden.

Ich musste für die Frontseite Daten von unterschiedlichen Newsfeeds zusammentragen und in der Webseite darstellen. Erschwehrend ist hinzu gekommen, das einige dieser Newsfeed-Quellen relativ träge/langsam sein können. Ein Caching musste also her.

Ich habe mich für eine Multithreading-Lösung entschieden, indem ich im Global.asax-File bei Start der Applikation (Application Pool) einen Worker-Thread starte, welcher alle paar Minuten die Neewsfeed-Quellen abgrasst und innerhalb des lokalen App_Data-Verzeichnisses als XML-Datei speichert. Die Funktion, welche diese Newsfeeds ausliesst und lokal als XML-Datei speichert, sieht folgendermassen aus:

    /// 
    /// Retrieve the RSS feed by URL and stores it in a local file within the directory App_Data.
    /// 
    /// The feed URL.
    /// The app data filename (without path).
    private static void UpdateLocalFeed(String feedUrl, String appDataFilename)
    {
        if (feedUrl == null) throw new ArgumentNullException("feedUrl");
        if (appDataFilename == null) throw new ArgumentNullException("appDataFilename");

        var reader = XmlReader.Create(feedUrl);
        var feed = SyndicationFeed.Load(reader);
        reader.Close();
        var filename = HostingEnvironment.MapPath(String.Format("~/App_Data/{0}", appDataFilename));

        if ((feed != null) && (!String.IsNullOrEmpty(filename)))
        {
            using (var writer = XmlWriter.Create(filename))
            {
                if (writer != null)
                {
                    feed.SaveAsRss20(writer);
                }
            }
        }
    }

Aufgerufen wird diese dann z.B. folgendermassen:

    private static void UpdateTauchenFeed()
    {
        UpdateLocalFeed("http://blog.marcduerst.com/category/feed/Tauchen.aspx", "diving.xml");
    }

Wie zu sehen ist, habe ich zwei sehr schöne .Net-API's miteinander kombiniert. Mittels der SyndicationFeed Klasse von .Net kann man sich relativ einfach eine Liste von FeedItem-Objekten holen. Jedes FeedItem-Objekt beinhaltet dabei Title, Summary, Author, Links, usw.) - alles schön typisiert. Anschliessend baue ich mir einen XmlWriter und speichere den Feed als RSS 2.0 Einträge in diesem XMLWriter.

Kleines Detail am Rande ist noch die folgende Zeile, welche vie HostingEnvironment den MapPath() aufruft anstatt via Server-Klasse. Die Server-Klasse ist via HttpContext erreichbar. Diese haben wir hier aber nicht zur Verfügung weil wir ja in unserem Background-Worker (Thread) sind. Somit steht nur HostingEnvironemnt für MapPath() zur Verfügung:

        var filename = HostingEnvironment.MapPath(String.Format("~/App_Data/{0}", appDataFilename));

Nun wollen wir den Feed aus der zuvor lokal gespeicherten XML-Datei wieder laden und ausgeben. Für ersteres habe ich mir folgende Funktion erstellt:

    /// 
    /// Reads a RSS feed from a previously stored local file within the App_Data directory.
    /// 
    /// The app data feed filename (without path).
    /// 
    private static SyndicationFeed GetLocalFeed(string appDataFeedFilename)
    {
        if (appDataFeedFilename == null) throw new ArgumentNullException("appDataFeedFilename");

        var filename = HostingEnvironment.MapPath(String.Format("~/App_Data/{0}", appDataFeedFilename));
        if ((filename != null) && (File.Exists(filename)))
        {
            using (var reader = XmlReader.Create(filename))
            {
                var result = SyndicationFeed.Load(reader);
                return result;
            }
        }

        return null;
    }

Hier bauen wir uns erst einen XMLReader mit dem entsprechenden Dateinamen. Anschliessend lassen wir die SyndicationFeed Klasse aus diesem XML-File lesen und den resultierenden Feed an den Aufrufer zurück. Angewendet wird das ganze im Code-Behind unserer ASP.Net WebForm Seite (z.B. default.aspx.cs):

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Get the latest dive blogs
            var diveFeed = FeedTools.GetLocalDiveFeed();
            _latestDive.DataSource = diveFeed.Items.Take(4);
            _latestDive.DataBind();
        }
    }

Die Objekt _latestDive ist ein ASP.Net Repeater welchem hier direkt der Newsfeed mittels Databinding gesetzt wird. Dann mittels DataBind() die Datenbindung ausführen und schon können wir die Daten ausgeben. Der Markup-Code für den obigen Repeater sieht dann folgendermassen aus:

        

Wie zu sehen ist, wird wieder direkt auf die Properties der FeedItem Objekte gebunden (Eval("Title.Text"))

Das Syndication-API von .Net hat sich als erstaunlich einfach und pain-free herausgestellt, welches auch unterschiedlichste Quellen und Formate unterstützt. Zusammen mit dem XML-Writer und -Reader eine super Kombination.