NotificationObject

Jeder kennt das Problem mit den "Magic Strings" mit INotifyPropertyChanged. Ich denke auch Lösungen kennen bereits die meisten. Ich möchte hier ein paar Lösungen vorstellen die ich bereits kenne.

image

So, wie es wahrscheinlich jeder kennen dürfte. Im Setter wird das OnPropertyChanged-Event geworfen, der einzige Nachteil hier ist, die Bezeichnung des Propertynamen wird als String übergeben. Beim Umbenennen des Properties wird oft vergessen den Übergabeparameter (Property-Namen) zu ändern. Dafür gibt es mehrere Möglichkeiten dies zum umgehen.

Variante 1:

Hierbei erweitern wir jede Klasse um weitere Methoden. Das Zauberwort hierfür lautet Lambda-Expressions.

image

Das war mein erster Versuch, den ich lange in meinen  Objekten hatte, aber so richtig glücklich war ich mit der Entwicklung noch nicht. Zumindest die “Magic Strings” sind verschwunden, allerdings hatte ich in jeder Klasse die 3 oben genannten Methoden. Lange hab ich DRY missachtet und irgendwann mir eine Lösung überlegt. 

image

 

Variante 2:

Bzw. ist das eine Weiterentwicklung der Variante 1. Ich hab mir ein NotificationObject erstellt und diese abstrakte Klasse von INotifyPropertyChanged abgelitten.

image

Jetzt muss nur noch das Objekt vom NotificationObject erben und im Setter jedes Properties müssen folgende Zeilen eingefügt werden.

image

So weit so gut, jetzt haben wir nur noch das Problem, dass die Prüfung des Wertes auch noch in jedem Setter vorhanden ist. Auch diese lagern wir noch in die Basisklasse aus.

Fast fertig, das einzige was mich noch stört ist die Prüfung ob sich der Wert geändert hat. In jedem Property muss ich diese Prüfung vornehmen. Deswegen hab ich mich für eine Erweiterung der Klasse NotificationObject entschieden. Es gibt eine weitere “RaisePropertyChanged”-Methode der man den aktuellen und den neuen Wert übergeben kann. Diese Methode ruft eine SetValue-Methode auf und prüft ob der Wert sich geändert hat und setzt diesen. Das ganze könnte dann in etwa so aussehen.

image

 image

Der Aufruf der Methode sind dann folgend aus:

image

Aus Clean-Code-Sicht könnte man jetzt noch über die Verwendbarkeit diskutieren, z.B. muss das Prüfen und Setzen des neuen Wertes in einer eigenen Methode erfolgen. Vorerst belasse ich diese Methode für mich, hab aber jederzeit ein offenes Ohr für Anregungen und Verbesserungen. Smiley

Variante 3:

Die dritte Variante richtet sich an bereits vorhandenen Frameworks die ein NotificationObject beinhalten. Ich selbst hab die Frameworks noch nicht eingesetzt, aber einiges dazu gelesen. Für mich ist es derzeit Overhead.

  • MS Prism
  • MVVM Light Toolkit

Beide Frameworks bieten ein NotificationObject und vieles vieles mehr. Aber wie gesagt für mich einfach Overhead, da ich viele Funktionen nicht benötige.

Variante 4:

Eigentlich keine richtige Variante, sondern nur ein Hilfsmittel. Wer den ReSharper ab der 7. Version verwendet, hat die Möglichkeit per Attribute eine Warnung des ReSharper’s zu bekommen.

image

Der ReSharper schlägt automatisch beim Anlegen der Methode den Namen vor.
Ändert man die Methode erscheint eine gelbe Lampe vom ReSharper.

Fazit:

Es gibt viele Möglichkeiten für eine saubere Implementierung von INotifyPropertyChanged. Viele Frameworks unterstützen es bereits, allerdings für mich uninteressant, da es für mich einfach Overhead bedeutet.  

Die Zeiten ändern sich

Lang ist es her, dass ich einen Beitrag geschrieben habe. Ich hatte mir eigentlich mal vorgenommen jede Woche einen Blogeintrag zu veröffentlichen. Leider hatte ich es nie geschafft es wirklich umzusetzen, meistens aus Zeitgründen. Trotzdem hab ich weiterhin interessiert andere Blogs gelesen und musste feststellen, die Zeiten ändern sich. Ich hab das Gefühl vor Jahren waren die technischen Themen im Vordergund und heute geht es immer mehr um Agilität in der Sotwareentwicklung. Während man früher über Pattern usw. “gestolpert” ist, findet man heute eher nur noch Scrum, Kanban, usw. Vllt. täusche ich mich, aber ich hab das Gefühl die technischen Themen werden immer weniger. Und ist das nicht unser Handwerkszeug?

Versteht mich nicht falsch, ich bin für agile Methoden. Selbst bei kleinen Projekten versuche ich ein Mini-Kanban einzusetzen. Ich benutze hierfür 3 Spalten “ToDo”, “In Work”, “Done”. Und es hat sich bewährt. Aber ich denke hier muss man den Überblick behalten und nicht übertreiben. Letztendlich ist doch die Software die wir produzieren für den Kunden wichtig und nicht das Vorgehen (im weitesten Sinne).

Ich möchte mit dem Blog eine Brücke schlagen zwischen Agilität und Code, beides vereint aufzeigen und hoffe auf rege Diskussionen :-).

Logging mit Log4Net

Ich weiß, es gibt viele Artikel über das Loggen mit Log4Net. 🙂
Trotzdem werde ich anhand eines Beispiels das Loggen mit Log4Net zeigen.

Als erstes schauen wir uns die Konfigurations-Datei an.

   1: <?xml version="1.0" encoding="utf-8" ?>

   2: <configuration>

   3:   

   4:   <configSections>

   5:     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />

   6:   </configSections>

   7:   

   8:   <log4net>

   9:     <appender name="FileAppender" type="log4net.Appender.RollingFileAppender">

  10:       <file value="log\log-file" />

  11:       <appendToFile value="true" />

  12:       <rollingStyle value="Date" />

  13:       <datePattern value="yyyyMMdd" />

  14:       

  15:       <layout type="log4net.Layout.PatternLayout">

  16:         <conversionPattern value="%date [%thread] %-5level %logger [%property{Session}] - %message%newline" />

  17:       </layout>

  18:     </appender>

  19:     

  20:     <root>

  21:       <level value="ALL" />

  22:       <appender-ref ref="FileAppender" />

  23:     </root>

  24:   </log4net>

  25:   

  26: </configuration>

Appender:

Ich verwende hier den RollingFileAppender, dieser kann nach Größe und Datum eingeschränkt werden. Ist die Größe erreicht bzw. hat sich das Datum geändert, wird eine neue Log-Datei angelegt.  In dem Beispiel werden die Log-Files in einem Unterverzeichnis “log”

[file value=log\log-file”] im Programmverzeichnis abgelegt. Mit rollingStyle lege ich fest wann eine neue Datei erstellt werden soll. Hier wird jeden Tag eine Log-File erstellt. Festgelegt wird das mit dem datePattern.

Weitere Appender sind hier nachschlagbar.

Layout:

Für das Layout verwende ich das Patternlayout, ein flexibles konfigurierbares Layout. Weitere Layouts gibt es hier.

Unter conversationPattern kann die Ausgabe definiert werden. Welche Möglichkeiten für die Ausgabe vorhanden sind, findet man hier.

Im letzten Abschnitt wird bestimmt wie und was geloggt werden soll. Folgende Level-Stufen sind verfügbar:

– Off

– Fatal

– Error

– Warn

– Info

– Debug

– All

Mit appender-ref wird der oben definierte Appender verwendet. Es können natürlich mehrere Appender definiert werden.

Verwendung im Code:

   1: static void Main()

   2: {

   3:     log4net.Config.XmlConfigurator.Configure(new FileInfo("logging.config"));

   4:     ILog logger = LogManager.GetLogger(typeof(Program));

   5:      

   6:     logger.Info("Info");

   7:     logger.Debug("Debug");

   8:     logger.Error("Error");

   9:     logger.Fatal("Fatal");

  10:     logger.Warn("Warn");

  11: }

In der dritten Zeile wird log4net konfiguriert, hierfür geben wir die “Config-Datei” an. Eine Zeile weiter wird der Logger geholt. Für jedes Level gibt es eine Methode.

Level:

Vielleicht sollte noch die Hierarchie verschiedener Levels erwähnt werden.

Info

   1: 2009-10-11 18:36:32,305 [10] INFO  WindowsFormsApplication1.Program [(null)] - Info

   2: 2009-10-11 18:36:32,339 [10] ERROR WindowsFormsApplication1.Program [(null)] - Error

   3: 2009-10-11 18:36:32,340 [10] FATAL WindowsFormsApplication1.Program [(null)] - Fatal

   4: 2009-10-11 18:36:32,340 [10] WARN  WindowsFormsApplication1.Program [(null)] - Warn

Debug

   1: 2009-10-11 18:37:23,195 [9] INFO  WindowsFormsApplication1.Program [(null)] - Info

   2: 2009-10-11 18:37:23,228 [9] DEBUG WindowsFormsApplication1.Program [(null)] - Debug

   3: 2009-10-11 18:37:23,228 [9] ERROR WindowsFormsApplication1.Program [(null)] - Error

   4: 2009-10-11 18:37:23,228 [9] FATAL WindowsFormsApplication1.Program [(null)] - Fatal

   5: 2009-10-11 18:37:23,228 [9] WARN  WindowsFormsApplication1.Program [(null)] - Warn

Error

   1: 2009-10-11 18:37:44,140 [9] ERROR WindowsFormsApplication1.Program [(null)] - Error

   2: 2009-10-11 18:37:44,173 [9] FATAL WindowsFormsApplication1.Program [(null)] - Fatal

Anhand der Beispiele kann man erkennen, dass nicht nur das eingestellt Level geloggt, sondern hierarchisch vorgegangen wird.

Fazit:

Log4Net ist einfach und schnell einzusetzen. Durch die riesige Auswahl an Möglichkeiten des Loggings ist sicherlich für jeden was dabei.

Viel Spaß beim Loggen! 🙂

NHibernate – typisierte Liste in ein DTO wandeln

Wie kann man ganz einfach eine generische Liste in ein “generisches DTO” wandeln? Diese Frage stellten wir uns letztens in der Arbeit. Nach mehreren Codezeilen um die generische Liste (manuell) zu mappen, waren wir der Meinung, dass es doch einfacher zu handhaben sein muss.
Nach einer längeren Recherche sind wir dann auf Transformers.AliasToBean gestoßen. Anbei ein kleines Beispiel für die Anwendung von Transformers.AliasToBean.

So könnte die Klasse “Auflage” aussehen:

image

Das dazugehörige DTO:

image

Da ich Fluent-NHibernate verwende, wird die Klasse wie folgt gemappt:

   1: public class AuflagenMap : ClassMap<Auflage>

   2: {

   3:     public AuflagenMap(){

   4:         Id(x => x.Id);

   5:         Map(x => x.Datum);

   6:         Map(x => x.Anzahl);

   7:     }

   8: }


Mit Projections.Sum(“Anzahl”).As(“Auflage”) wird bestimmt wohin gemappt werden soll. “Anzahl” kommt aus “Auflage” und wird per Alias (“.As”) in das Property “Auflage gemappt. Das gleiche gilt natürlich auch für die Gruppierung nach Datum.

   1: public IEnumerable<AuflageDto> AuflagenSummierung(){    

   2:     var criteria = m_Session.CreateCriteria(typeof (Auflage))

   3:         .SetProjection(Projections.ProjectionList()

   4:             .Add(Projections.Sum("Anzahl").As("Auflage"))

   5:             .Add(Projections.GroupProperty("Datum").As("Datum")))

   6:         .AddOrder(Order.Asc("Datum"))

   7:         .SetResultTransformer(Transformers.AliasToBean(typeof (AuflageDto)))

   8:         .List<AuflageDto>();

   9:     return criteria;

  10: }

Das Ergebnis sieht dann so aus:

image

Ist das so einfach?

Ich beschäftige mich im Moment ein bisschen mit FluentNHibernate.
Das erste was ich mir angesehen habe, war die Testbarkeit.
Ich bin überrascht, kann es manchmal wirklich  so einfach sein?
Ein kleines Beispiel (nicht vollständig) anbei.

Benutzer:

1 public class BenutzerMap : ClassMap<Benutzer> 2 { 3 public BenutzerMap() 4 { 5 Id(x => x.Id); 6 Map(x => x.Vorname); 7 Map(x => x.Nachname); 8 } 9 }

Test:

1 [Test] 2 public void TestMapBenutzer() 3 { 4 new PersistenceSpecification<Benutzer>(m_Session) 5 .CheckProperty(c => c.Nachname, "Mustermann") 6 .CheckProperty(c => c.Vorname, "Max") 7 .VerifyTheMappings(); 8 }

Was passiert beim Test:

  • es wird eine Benutzer-Instanz erstellt
  • der Benutzer wird in der Datenbank/Tabelle angelegt
  • der gespeicherte Benutzer wird in eine neue Instanz geladen
  • die neue Instanz wird mit dem Original verglichen