code it

Martins Tech Blog

StyleCop Upgrade Dialog verhindern

StyleCop ist ein wirklich gutes Tool zur Unterstützung der Durchsetzung von Coding Guidelines. Mit Version 4.5 ist es auch nicht mehr notwendig zusätzlicher AddOns zu installieren, damit es sich automatisch in Resharper integriert - das übernimmt alles die Installationsroutine von StyleCop.
 
Seit der Installation beglückt mich StyleCop nun aber jeden Morgen mit der freudigen Nachricht, dass es wieder eine neue Version zum Herunterladen gibt, die ich doch bitte installieren soll. Vielleicht waren die Releasezyklen von StyleCop auch bisher so kurz und ich habe es nur nicht gemerkt, weil es dieses Check-Feature bisher nicht gab. Auf jeden Fall kann dieser Dialog ganz schön nervig sein, da er wenn man ihn denn mit Nein beantwortet, bei jedem Start von Visual Studio wieder kommt und es keine offensichtliche Möglichkeit gibt, diesen zu unterbinden.
 
Für das Problem gibt es zwei Lösungen:
Die erste Lösung setzt voraus, dass man Resharper installiert. In diesem Fall gibt es in den Optionen von Resharper unter dem Punkt Tools/StyleCop die Möglichkeit, einzustellen, ob und wann StyleCop nach Upgrades suchen soll. Im Standard ist hier "Every Time Visual Studio starts" aktiviert.
Die zweite Lösung ist das manuelle Setzen der Einstellungen in der Registry. Die Einstellungen finden sich dort unter HKEY_CURRENT_USER\Software\CodePlex\StyleCop.
 
Wenn man der Diskussion auf Codeplex und der dort genannten Releaseplanung Glauben schenken darf, wird es in Version 5.0 von Stylecop auch eine Möglichkeit geben, diese Einstellung direkt über einen von StyleCop mitgelieferten Konfigurationsdialog zu editieren (für all die Anwender, die kein Resharper haben).

RSS-Feeds an eigene Bedürfnisse anpassen

In meinem letzten Post hab ich eine kleine Einführung in das Thema RSS-Feeds mit .NET erzeugen gegeben. Nun sind aber nicht alle Inhalte, die man per RSS-Feed übertragen möchte in den Standard-Elementen unterzubringen, die die RSS-Spezifikation definiert. Typische Beispiele dafür sind zusätzliche Geodaten oder Daten zu Medieninhalten in Podcasts. Um auch dieser Anforderung gerecht zu werden, bieten die beiden vorgestellten Klassen SyndicationFeed und SyndicationItem noch die Möglichkeit eigene Attribute oder eigene Elemente zu definieren. Wie das geht, soll an einem kleinen Beispiel gezeigt werden:

Eigene Elemente zum Item hinzufügen

Möchte man einfach nur zusätzliche Elemente zu den Items hinzufügen, kommt man recht schnell ans Ziel. Schlüssel sind die ElementExtensions. Im Beispiel werden jedem Item noch die Elemente Latitude und Longitude zur Definition einer Geokoordinate hinzugefügt.

var item = new SyndicationItem
{
    Id = "http://myfeeds/2011/06/" + i,
    Title = SyndicationContent.CreatePlaintextContent("Wichtige Mitteilung"),
};

item.ElementExtensions.Add("Latitude", null, 34.2543);
item.ElementExtensions.Add("Longitude", null, 14.2545);

Das Resultat ist, dass wie gewünscht das Item-Element über die definierten Sub-Elemente verfügt.

<item>
    <guid isPermaLink="false">http://myfeeds/2011/06/0</guid>
    <title>Wichtige Mitteilung</title>
    <Latitude>34.2543</Latitude>
    <Longitude>14.2545</Longitude>
</item>

Den gleichen Weg kann man dann auch beim Einlesen der Daten wieder verwenden: In der Auflistung ElementExtensions befinden sich die hinzugefügten Elemente mit den jeweiligen Namen und Inhalten. Mit Hilfe der generischen Methode GetObject kann man dann auf die Inhalte zugreifen.

var feed2 = SyndicationFeed.Load(reader);
foreach (var item in feed2.Items)
{
    var latitudeElementExtension = item
        .ElementExtensions
        .Single(e => e.OuterName == "Latitude");
    var latitude = latitudeElementExtension.GetObject<double>();
}

Analog zu den ElementExtensions gibt es die AttributeExtensions, die (wie sollte es anders sein) keine Elemente sondern Attribute erzeugen.

Eigene komplexe Objekte zum Item hinzufügen

ElementExtensions erlauben nicht nur das Hinzufügen primitiver Datentypen sondern auch komplexer Objekte - Bedingung: Sie müssen xml-serialisierbar sein und es muss ein passender XML-Serializer existieren oder es muss sich um einen DataContract handeln. In meinem Fall definiere ich mir nun eine neue Klasse GeoCoordinate, die sich als Location serialisiert.

[XmlRoot("Location", Namespace = "http://ns.uniquesoftware.de/location")]
[Serializable]
public class GeoCoordinate
{
public GeoCoordinate()
{
}

 public GeoCoordinate(double lat, double lon)
 {
     Latitude = lat;
     Longitude = lon;
 }

 [XmlElement("Latitude")]
 public double Latitude { get; set; }
 [XmlElement("Longitude")]
 public double Longitude { get; set; }
}

Im Anschluss definiere ich wie vorhin auch eine ElementExtension und füge dieser der Auflistung des Elements hinzu.

var item = new SyndicationItem
{
    Id = "http://myfeeds/2011/06/" + i,
    Title = SyndicationContent.CreatePlaintextContent("Wichtige Mitteilung"),
};
item.ElementExtensions.Add(
    new GeoCoordinate(34.2543, 14.2545), 
    new XmlSerializer(typeof(GeoCoordinate)));

Das resultierende XML im Feed beinhaltet nun wie erwartet die eben definierten Tags, die über die definierten Namespaces verfügen.

<item>   
    <guid isPermaLink="false">http://myfeeds/2011/06/0</guid>
    <title>Wichtige Mitteilung</title>
    <Location 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns="http://ns.uniquesoftware.de/location">
        <Latitude>34.2543</Latitude>
        <Longitude>14.2545</Longitude>
    </Location>
</item>

Eigene Namespaces verwenden

Häufig trifft man auf Konstrukte, in denen der Namespace nicht explizit jedes Mal wieder als Attribut im Element definiert wird, sondern wo nur noch eine Namespace-Referenz existiert. Nichts einfacher als das: Dazu fügt man einfach die XML-Namespace-Deklaration der AttributeExtensions-Auflistung des SyndicationFeed-Objektes hinzu. Im folgenden Beispiel habe ich dafür den Namespace "my" deklariert.

// create new syndication feed 
var feed = new SyndicationFeed(
    "Mein Blog Feed",
    "Wissenwertes über ASP.NET, WPF und Silverlight",
    new Uri("http://feeds.feedburner.com/discoveringmicrosofttechnologies"))
    {
        Language = "de-DE",
        Copyright = new TextSyndicationContent("Unique Software"),
        Generator = "Unique Software News Generator"
    };
feed.AttributeExtensions.Add(
    new XmlQualifiedName("my", "http://www.w3.org/2000/xmlns/"), 
    "http://ns.uniquesoftware.de/location");

Der Effekt ist, dass nun der Namespace im XML-Element channel definiert und dann automatisch eine Namespace-Referenz aufgebaut wird.

<item>
    <guid isPermaLink="false">http://myfeeds/2011/06/0</guid>
    <title>Wichtige Mitteilung</title>
    <my:Location>
        <my:Latitude>34.2543</my:Latitude>
        <my:Longitude>14.2545</my:Longitude>
    </my:Location>
</item>

Zusammenfassung

ElementExtensions und AttributeExtensions sind eine gute Möglichkeit, RSS-Feeds flexibel auf die eigenen Bedürfnisse anzupassen und mit Hilfe des Objektmodells typsicher auf die Objekte beinhalteten Objekte zuzugreifen. Besonders bei XML-Strukturen, die nur noch sehr entfernt etwas mit dem ursprünglichen Feed-Objektmodell zu tun haben, wird es wohl immer wieder eine Abwägung von Aufwand und Nutzen sein, ob man ElementExtensions und die Syndication-Objekte verwendet oder ob man die XML-Struktur "schnell eben selbst" per Linq-To-Xml zusammenbaut. Ich persönlich finde die sich bietenden Möglichkeiten wieder recht umfangreich und finde es schade, dass die Klassen so ein unbeachtetes Dasein fristen. 

Englische Version Englische Version

RSS-Inhalte mit .NET erzeugen

Das Erzeugen einer XML-Struktur, die der RSS-Spezifikation entspricht ist gar nicht so kompliziert - erst recht nicht, seitdem man mit Linq-To-Xml recht einfach durch XML-Dokumente navigieren und diese auch modifizieren kann. Nachteil ist: Man muss die Spezifikation immer griffbereit haben, denn ganz schnell kommt man zu Fragen wie: "Wie hieß noch gleich das XML-Element für das Erstellungsdatum und in welchem Format muss es ausgegeben werden?" Was bisher aber nur wenige zu wissen scheinen: So viel Arbeit muss man sich gar nicht mehr machen. Seit dem .NET Framework 3.5 gibt es den Namespace System.ServiceModel.Syndication, dessen Klassen einen Großteil der Arbeit abnehmen. Die Erstellung ist ganz einfach: Zunächst erstellt man sich ein Objekt vom Typ SyndicationFeed. Dieses Objekt repräsentiert den eigentlichen Feed und dessen Eigenschaften.
 
// create new syndication feed  
var feed = new SyndicationFeed("Mein Blog Feed",
 "Wissenwertes über ASP.NET, WPF und Silverlight",
 new Uri("http://feeds.feedburner.com/discoveringmicrosofttechnologies"))
  {
     Language = "de-DE",
     Copyright = new TextSyndicationContent("Unique Software"),
     Generator = "Unique Software News Generator"
  };
 
Die Klasse SyndicationItem ermöglicht es dann, ebenfalls über das Objektmodell, die eigentlichen Einträge zu erzeugen, die mit Hilfe des Feeds übermittelt werden sollen. Diese werden dann dem Feed-Objekt nur noch zugewiesen.
 
// create syndication items which should be delivered
var items = new List<SyndicationItem>();
for (var i = 0; i < 5; i++)
{ 
 var item = new SyndicationItem
     {
         Id = "http://myfeeds/2011/06/" +  i,
         PublishDate = DateTime.Now,
         Title = SyndicationContent
             .CreatePlaintextContent("Wichtige Mitteilung"),
         Content = SyndicationContent
             .CreateHtmlContent("Meine wichtige Nachricht an alle Leser"),
         Summary = SyndicationContent
             .CreatePlaintextContent("Zusammenfassung meiner Mitteilung")
     };
     items.Add(item);
}
feed.Items = items;
 
Das war's auch schon fast: Letzter Schritt ist die Umwandlung in XML. Dafür gibt es auf dem Feed-Objekt die Methoden SaveAsAtom10 und SaveAsRss20, die diese Arbeit übernehmen.
 
var builder = new StringBuilder();
using (var writer = XmlWriter.Create(builder))
{
 feed.SaveAsRss20(writer);
 writer.Flush();
 writer.Close();
}
var xml = builder.ToString();
 
Im Hintergrund kommen hier je nach Methode die Atom10FeedFormatter-Klasse bzw. die Rss20FeedFormatter-Klasse zum Einsatz. Und mit diesem XML kann man nun das gleiche machen, was man sonst mit dem XML gemacht hätte, das man mit Linq-To-Xml zusammengebaut hätte: Man liefert es aus.

Englische Version Englische Version