Dojos für Entwickler. Stefan Lieser
Чтение книги онлайн.
Читать онлайн книгу Dojos für Entwickler - Stefan Lieser страница 11
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.