Dojos für Entwickler. Stefan Lieser

Читать онлайн книгу.

Dojos für Entwickler - Stefan Lieser


Скачать книгу
aus Zeitgründen jedoch verzichtet. Übergangsweise kann man sich damit behelfen, die CSV-Dateien mit einem ETL-Prozess (Extract, Transform, Load) in die Datenbank zu schaufeln.

      Die Komponenten Generators, Data-Pump, DbDataAdapter und CsvDataAdapter haben nur geringe Abhängigkeiten, wie Abbildung 6 zeigt. Der CsvDataAdapter ist nicht von den anderen Komponenten abhängig, weil er lediglich auf dem gemeinsamen Datenmodell aufsetzt.

      [Abb. 6] Abhängigkeitsdiagramm der Komponenten.

      Einzelne Werte

      Für das Erzeugen eines einzelnen Wertes habe ich mich für die Verwendung eines Generators entschieden. Dieser hat die Aufgabe, zu einem gegebenen Typ einen Wert zu liefern. Die dabei verwendete Strategie bestimmt der Generator. So ist ein Generator denkbar, der zufällige Werte erzeugt. Genauso kann aber auch ein Generator erstellt werden, der eine Liste von Daten erhält und daraus zufällig auswählt.

      Die Beschreibung der zu erzeugenden Datenzeilen besteht also darin, pro Spalte einen Generator zu definieren. Ferner wird pro Spalte der Name der Spalte benötigt.

      Im Kontrakt der Generatoren habe ich einen generischen Typparameter verwendet, siehe Listing 1. Dadurch wird bereits zur Übersetzungszeit geprüft, ob der Rückgabewert der Methode GenerateValue zum Generatortyp passt.

      Listing 1: Einen generischen Typparameter verwenden.

       public interface IGenerator<T>

       {

       T GenerateValue();

       }

      Die Generatoren werden in den Spaltendefinitionen verwendet. Da sie einen generischen Typparameter haben, muss dieser bei Verwendung des Generators entweder durch einen konkreten Typ oder an derVer-wendungsstelle durch einen generischen Typparameter belegt werden. Für die Klasse ColumnDefinition würde das bedeuten, dass diese ebenfalls einen generischenTyp-parameter erhält, siehe Listing 2.

      Listing 2: Spalten definieren.

       public class ColumnDefinition<T>

       {

       public ColumnDefinition(string columnName, IGenerator<T> generator)

       {

       ColumnName = columnName;

       Generator = generator;

       }

       public string ColumnName { get; private set; }

       public IGenerator<T> Generator { get; private set; }

       }

      So weit, so gut. Doch eine Zeile besteht aus mehreren Spalten. Daher müssen mehrere ColumnDefinition<T>-Objekte in einer Liste zusammengefasst werden. Da natürlich jede Spalte einen anderen Typ haben kann, muss es möglich sein, beispielsweise eine ColumnDefinition<string> sowie eine ColumnDefinition<int> in diese Liste aufzunehmen. Dies ist jedoch mit C# 3.0 aufgrund der fehlenden Ko-/Kontravarianz noch nicht möglich. Würde man die Liste als List<object> definieren, müsste die Liste Kovarianz unterstützen. Das tut sie jedoch nicht, Ko- und Kontravarianz stehen erst mit C# 4.0 zur Verfügung. Ich habe daher den Generator in der ColumnDefinition als IGenerator<object> definiert, statt ColumnDefinition generisch zu machen. Dies kann man dann mit Erscheinen von Visual Studio 2010 ändern.

      Eine Zeile

      Durch die Generatoren können die einzelnen Werte der Spalten erzeugt werden. Um eine ganze Datenzeile zu erzeugen, muss jeder Generator einmal aufgerufen werden, um seinen jeweils nächsten Wert zu liefern. Dies ist bei Verwendung von LINQ ganz einfach, siehe Listing 3.

      Listing 3: Werte erzeugen.

       public static IEnumerable<object> GenerateValues(this IEnumerable<ColumnDefinition>

       columnDefinitions) {

       return columnDefinitions

       .Select(x => x.Generator)

       .Select(x => x.GenerateValue());

       }

      In der Methode wird über die Aufzählung der Spaltendefinitionen iteriert und durch das erste Select jeweils der Generator aus der Spaltendefinition entnommen. Durch das zweite Select wird aus jedem Generator ein Wert abgerufen. Das Ergebnis ist eine Aufzählung der von den Generatoren gelieferten Werte. Diese Aufzählung wird später an den Konstruktor einer Zeile übergeben, siehe Listing 4.

      Listing 4: Eine Datenzeile generieren.

       public static Line GenerateLine(this IEnumerable<object> values)

       {

       return new Line(values);

       }

      Mehrere Zeilen

      Die Erzeugung mehrerer Zeilen erfolgt in einer for-Schleife. Dabei wird die Schleife so oft durchlaufen, dass die Anzahl der gewünschten Datensätze erzeugt wird. Dabei kommt wieder einmal ein yield return zum Einsatz, siehe Listing 5.

      Listing 5: Mehrere Zeilen generieren.

       public IEnumerable<Line> GenerateTestData(IEnumerable<ColumnDefinition>

       columnDefinitions, int rowCount)

       {

       for (var i = 0; i < rowCount; i++)

       {

       yield return

       columnDefinitions

       .GenerateValues()

       .GenerateLine();

       }

       }

      Flow

      Und schon wieder konnte ich einen Flow identifizieren. Die Aufzählung der ColumnDefinitions fließt in die Methode GenerateValues. Heraus kommt eine Aufzählung mit Werten. Diese wird weitergeleitet in die Methode GenerateLine, die aus den Werten eine Zeile erstellt:

      columnDefinitions

       .GenerateValues()

       .GenerateLine();

      Um den Flow so formulieren zu können, sind die beiden Methoden als Extension Methods realisiert. Dadurch wird das Aneinanderreihen der Methoden besonders einfach. Abbildung 7 zeigt den Flow.

      [Abb. 7] Flow zum Erzeugen einer Datenzeile.

      Damit ist die Komponente DataPump bereits beschrieben. Weiter geht es bei den Generatoren.

      Generatoren

      Ein Generator ist für das Erzeugen von Werten eines bestimmten Typs zuständig. Welche Strategie dabei verfolgt wird, ist Sache des Generators. Dies soll am Beispiel eines Generators für int-Werte gezeigt werden, der zufällige Werte innerhalb vorgegebener Minimum- und Maximumwerte erzeugt.

      Die Implementierung des Generators ist ganz einfach. Ich verwende einen Zufallszahlengenerator System.Random aus dem .NET Framework und weise ihn an, einen Wert innerhalb der definierten Grenzen zu liefern, siehe Listing 6. Die spannende Frage ist nun: Wie kann man einen solchen Generator testen, der zufällige Werte liefern soll? Sie werden bemerkt haben, dass oben im Listing der Zufallszahlengenerator random nirgendwo instanziert und zugewiesen wird. Dies liegt in der Notwendigkeit begründet, den Generator automatisiert testen zu können. Würde der Generator den Zufallszahlengenerator selbst instanzieren, würde er immer zufällige Werte liefern. Dies soll er natürlich tun, aber im Test benötigen wir die Kontrolle darüber, welche Werte „zufällig" geliefert werden, siehe Listing 7.

      Listing 6: Ein Generator für


Скачать книгу