Seit dem letzten Teil der Design Patterns, oder besser gesagt “Best Practives” Reihe ist schon einige Zeit vergangen. Im vorangegangen Artikel haben wir kurz einen Ausflug ins ASP.NET MVC Framework gemacht. Nun ist es an der Zeit an Persistenz und Datenzugriff zu denken, die folgenden 2 Artikel werden dieses Gebiet abdecken.
Wer Visual Studio verwendet kann natürlich die komplette “Infrastruktur” zum Datenzugriff mit Drag&Drop, zB. mittels LINQ to SQL Designer erzugen lassen. Wie es dann allerdings mit Testbarkeit und Wartbarkeit aussieht, muss nicht extra erwähnt werden. Wir wollen hier einen architektonisch sauberen Lösungsansatz verfolgen.
Unser Beispiel soll in folgende Layer partitioniert sein, welche zugleich den Projektmappen unserer Solution entsprechen.
| Business Layer | Die “dummen” Entitäten |
| Service Layer | Services setzten etwaige Businesslogik um |
| Repositories | Erledigen Persistenzaufgaben |
Anhand eines einfachen Beispiels gehen wir von “oben” (UI) bis nach ganz “unten” (Persistenz).
Als Framework kommt Castle ActiveRecord zum Einsatz, da es mit minimalem Konfigurationsaufwand verwendbar ist und die Vorgehensweise zur Entwicklung einer Persistenzschicht richtig umsetzt. Das heisst: Die Datenbank wird aus den Businessobjekten erzeugt und nicht umgekehrt.
Wir wollen Kunden und ihre Bestellungen darstellen und dazu noch ein bisschen Geschäftslogik, damit unsere Services etwas zu tun haben.
Wir werden den Aufbau unserer Lösung ganz einfach beginnen und dann später noch architektonisch verfeinern, also Refactoring betreiben.
Als erstes erstellen wir uns 2 neue Businessobjekte “Customer” und “Order”. Diese Businessobjekte sollten idealerweise nur dumme Datencontainer sein und keine Logik etc. enthalten. Die Klasse “Order” sieht dann vorläufig so aus:
1: public class Order
2: {
3: public Guid ID { get; set; }
4: public DateTime Date { get; set; }
5: public decimal OrderValue { get; set; }
6: public Customer Customer { get; set; }
7: }
8:
Den kompletten Sourcecode gibts unter “Downloads” als Archiv zum herunterladen, damit der Artikel nicht zu lange wird.
So weit, so gut; Um nun eine Entität vom Typ “Order” persistierbar zu machen, müssen wir ActiveRecord und unser Businessobjekt noch passend konfigurieren. ActiveRecord setzt auf dem bekannten NHibernate Framework auf und wird einfach über Attribute im Code verwendet. Die fertige Order Klasse sieht folgendermassen aus:
1: [ActiveRecord]
2: public class Order : ActiveRecordBase<Order>
3: {
4: [PrimaryKey]
5: public Guid ID { get; set; }
6: [Property]
7: public DateTime Date { get; set; }
8: [Property]
9: public decimal OrderValue { get; set; }
10: [Property]
11: public Customer Customer { get; set; }
12:
13: }
Für die Klasse Customer gehen wir gleich vor. Interessant ist noch die 1:n Beziehung Customer –> Order. Diese wird mit dem Attribut [BelongsTo] auf der einen Seite und [HasMany] auf der Gegenseite abgebildet. Also in der Klasse Order ist die Eigenschaft Customer mit [BelongsTo] markiert und in der Klasse Customers ist die Eigenschaft Orders mit [HasMany] markiert. (siehe Source). Folgendes Klassendiagramm ergibt sich nun, für das wir ein Schema erzeugen werden um die Daten zu persistieren.
Um das zu erreichen muss ActiveRecord noch für die entsprechende Datenquelle die erzeugt werden soll konfiguriert werden. Die Konfiguration geschieht mittels XML. Entweder als eigenen xml Datei oder als Sektion in der app.config. Unsere Konfiguration sieht folgendermassen aus (Für SQL Server 2005)
1: <?xml version="1.0" encoding="utf-8" ?>
2: <activerecord>
3: <config>
4: <add
5: key="connection.driver_class"
6: value="NHibernate.Driver.SqlClientDriver" />
7: <add
8: key="dialect"
9: value="NHibernate.Dialect.MsSql2005Dialect" />
10: <add
11: key="connection.provider"
12: value="NHibernate.Connection.DriverConnectionProvider" />
13: <add
14: key="connection.connection_string"
15: value="Data Source=.;Initial Catalog=PersistencyTest;Integrated Security=SSPI" />
16: </config>
17: </activerecord>
Die Initialisierung geht ganz einfach:
1: public static void Initialize()
2: {
3: IConfigurationSource config = new XmlConfigurationSource("ar.config");
4: var asm = Assembly.Load("PersistencySample.Business");
5: ActiveRecordStarter.Initialize(asm, config);
6: }
Unsere komplette Assembly PersistencySample.Business wird nach, mit [ActiveRecord] markierten Klassen durchsuch (Achtung: Diese Variante kann bei vielen Klassen in der Assembly sehr langsam sein!) –> Für erweiterte und andere Varianten der Initialisierung siehe den AR Configuration Guide (unter Links).
Das Ganze wird NATÜRLICH getestet…siehe Design Patterns Artikel – Teil 3 ! Diesmal mit MSTest: Ein neues Testprojekt hinzufügen und einfach mit Rechtsklick auf die entsprechende Methode einen Unit Test erzeugen lassen
1: [TestMethod()]
2: public void InitializeTest()
3: {
4: Application.Initialize();
5: Assert.IsTrue(true); //Wenn kein Fehler auftritt->success
6: }
Das Erzeugen des Schemas selbst ist ein Einzeiler:
1: public static void CreateSchema()
2: {
3: ActiveRecordStarter.CreateSchema();
4: }
Und MSTest gibt uns das grüne Licht !

Natürlich ist im Sinne von Test-Driven-Development die Verwendung eines Datenbanksystem nicht optimal. Eine Lösung hierfür ist die verwendung einer In-Memory Datenbank wie SQLite (ActiveRecord unterstützt diese).
Im nächsten Teil werden wir die Repositories implementieren und etwas Refactoring betreiben.
Downloads
Castle Active Record
Links
AR Configuration and Initialization