Entwurfsmuster für den Datenimport verschiedener Quelltypen und verschiedener Zieltypen

14

Ich muss ein Importskript (in C #) entwerfen und erstellen, das Folgendes verarbeiten kann:

  • Daten aus verschiedenen Quellen lesen (XML, XSLX, CSV)
  • Daten verifizieren
  • Schreiben Sie die Daten in verschiedene Objekttypen (Kunde, Adresse)

Die Daten stammen aus einer Reihe von Quellen, aber eine Quelle hat immer ein Importformat (entweder csv, xml, xslx). Importformate können von Quelle zu Quelle variieren. Neue Importformate können in Zukunft hinzugefügt werden. Die Zielobjekttypen sind immer gleich (Kunde, Adresse usw.).

Ich habe über die Verwendung von Generika nachgedacht und etwas über das Fabrikmuster gelesen, aber ich bin ein ziemlich großer Neuling in diesem Bereich, daher ist jeder Rat mehr als willkommen.

Was ist ein geeignetes Entwurfsmuster, um dieses Problem zu lösen?

jao
quelle
Halte es einfach.
NoChance

Antworten:

11

Sie gehen mit ausgefallenen Konzepten zu früh über Bord. Generika - wenn Sie einen Fall sehen, verwenden Sie sie, aber machen Sie sich sonst keine Sorgen. Fabrikmuster - noch viel zu viel Flexibilität (und zusätzliche Verwirrung).

Halte es einfach. Verwenden Sie grundlegende Praktiken.

  1. Versuchen Sie sich die Gemeinsamkeiten zwischen einem Lesevorgang für XML und einem Lesevorgang für CSV vorzustellen. Dinge wie, nächste Aufnahme, nächste Zeile. Da neue Formate hinzugefügt werden können, versuchen Sie, sich Gemeinsamkeiten vorzustellen, die das zu bestimmende Format mit den bekannten haben würde. Verwenden Sie diese Gemeinsamkeit und definieren Sie eine Schnittstelle oder einen Vertrag, an den sich alle Formate halten müssen. Obwohl sie sich an die Gemeinsamkeiten halten, können sie alle ihre spezifischen internen Regeln haben.

  2. Versuchen Sie, zum Validieren der Daten eine Möglichkeit bereitzustellen, neue oder andere Validator-Codeblöcke einfach einzufügen. Versuchen Sie also erneut, eine Schnittstelle zu definieren, in der jeder Prüfer, der für eine bestimmte Art der Datenerstellung verantwortlich ist, einem Vertrag entspricht.

  3. Bei der Erstellung der Datenkonstruktionen werden Sie wahrscheinlich mehr von dem abhängig sein, der die vorgeschlagenen Ausgabeobjekte entwirft. Versuchen Sie herauszufinden, was der nächste Schritt für die Datenobjekte ist, und gibt es Optimierungen, die Sie vornehmen können, wenn Sie die endgültige Verwendung kennen. Wenn Sie beispielsweise wissen, dass die Objekte in einer interaktiven Anwendung verwendet werden, können Sie dem Entwickler dieser App helfen, indem Sie 'Summierungen' oder Zählungen der Objekte oder andere Arten von abgeleiteten Informationen bereitstellen.

Ich würde sagen, die meisten davon sind Vorlagenmuster oder Strategiemuster. Das ganze Projekt wäre ein Adaptermuster.

Andyz Smith
quelle
+1, insbesondere für den ersten Absatz (und es ist schön zu sehen, dass Sie zu dem gleichen Schluss gekommen sind wie ich im letzten Absatz).
Doc Brown
Denken Sie auch an die Architektur des gesamten Projekts, um ein Format an ein anderes anzupassen. Können Sie sich eine Situation vorstellen, in der jemand nur einen Teil davon in einem anderen Projekt verwendet? ZB kommt möglicherweise ein neuer Datenvalidator auf den Markt, der nur mit SQL Server funktioniert. Jetzt müssen Sie nur noch die benutzerdefinierte XML-Datei lesen und in SQL Server einfügen, und die restlichen Schritte überspringen.
Andyz Smith
Um dies zu erleichtern, sollten die Teile nicht nur ihre internen Verträge haben, an die sie gebunden sind, sondern es sollte auch eine Reihe von Verträgen geben, die die Interaktion zwischen den Teilen definieren .
Andyz Smith
@AndyzSmith - Ich habe das gleiche Problem in meinem Code. Ich habe alles über Ihren Code mit Ausnahme des Adaptermusters verstanden. Wenn Sie sagten, das gesamte Projekt sei ein Beispiel für ein Adaptermuster, können Sie das veranschaulichen?
Gansub
9

Die offensichtliche Sache ist, Strategiemuster anzuwenden . Haben Sie eine generische Basisklasse ReadStrategyund für jedes Eingabeformat einer Unterklasse wie XmlReadStrategy, CSVReadStrategyetc. Dies ermöglicht es Ihnen , die Importverarbeitung von der Verifizierung der Verarbeitung und der Ausgabeverarbeitung unabhängig zu ändern.

Abhängig von den Details kann es auch möglich sein, die meisten Teile des generischen Imports beizubehalten und nur Teile der Eingabeverarbeitung auszutauschen (zum Beispiel das Lesen eines Datensatzes). Dies kann dazu führen, dass Sie das Muster der Vorlagenmethode erhalten .

Doc Brown
quelle
Bedeutet das, dass ich beim Verwenden des Strategiemusters separate Methoden zum Konvertieren der Objekte (Kunde, Adresse) von der Quelle zum Ziel erstellen muss. Ich möchte jedes Objekt lesen, konvertieren, validieren und in eine Liste aufnehmen, damit die Liste später in der Datenbank gespeichert werden kann.
Jao
@jao: Nun, wenn Sie meine Antwort noch einmal lesen, sehen Sie, dass mein Vorschlag darin bestand, "ReadStrategy" und nicht "ConvertStrategy" zu erstellen. Sie müssen also nur verschiedene Methoden zum Lesen von Objekten schreiben (oder welcher zusätzliche Teil Ihres Prozesses für das jeweilige Dateiformat individuell ist).
Doc Brown
7

Ein geeignetes Muster für ein Importdienstprogramm, das Sie möglicherweise in Zukunft erweitern müssen, wäre die Verwendung von MEF - Sie können den Speicherverbrauch niedrig halten, indem Sie den Konverter, den Sie im Handumdrehen benötigen, aus einer Lazy List laden und MEF-Importe erstellen, die mit Attributen dekoriert sind Dies hilft bei der Auswahl des richtigen Konverters für den Import, den Sie ausführen möchten, und bietet eine einfache Möglichkeit, die verschiedenen importierenden Klassen voneinander zu trennen.

Jeder MEF-Teil kann so erstellt werden, dass er eine Importschnittstelle mit einigen Standardmethoden erfüllt, die eine Zeile der Importdatei in Ihre Ausgabedaten konvertieren oder eine Basisklasse mit der Basisfunktionalität überschreiben.

MEF ist ein Framework für die Erstellung einer Plug-In-Architektur. So werden Outlook und Visual Studio erstellt. Alle diese reizvollen Erweiterungen in VS sind MEF-Teile.

Um eine MEF-App (Managed Extensability Framework) zu erstellen, müssen Sie zunächst einen Verweis auf einfügen System.ComponentModel.Composition

Definieren Sie Schnittstellen, um festzulegen, was der Konverter tun soll

public interface IImportConverter
{
    int UserId { set; }        
    bool Validate(byte[] fileData, string fileName, ImportType importType);
    ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}

Dies kann für alle zu importierenden Dateitypen verwendet werden.

Hinzufügen von Attributen zu einer neuen Klasse, die definieren, was die Klasse "exportieren" soll

[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
 ...interface methods...
}

Dies würde eine Klasse definieren, die CSV-Dateien (eines bestimmten Formats: Format1) importiert und über benutzerdefinierte Attribute verfügt, mit denen die Metadaten der MEF-Exportattribute festgelegt werden. Sie wiederholen dies für jedes Format oder jeden Dateityp, den Sie importieren möchten. Sie können benutzerdefinierte Attribute mit einer Klasse wie der folgenden festlegen:

[MetadataAttribute]
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ImportAttribute : ExportAttribute
{
    public ImportAttribute(ImportType importType, ImportFileType fileType, string customerUID)
        : base(typeof(IImportConverter))
    {
        ImportType = importType;
        FileType = fileType;
        CustomerUID = customerUID;
    }

    public ImportType ImportType { get; set; }
    public ImportFileType FileType { get; set; }
    public string CustomerUID { get; set; }
}

Um die MEF-Konverter tatsächlich zu verwenden, müssen Sie die MEF-Teile importieren, die Sie beim Ausführen Ihres Konvertierungscodes erstellen:

[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();

catalog sammelt die Teile aus einem Ordner, Standard ist der App-Speicherort.

converters ist eine Lazy-Liste der importierten MEF-Teile

Wenn Sie dann wissen, welche Art von Datei Sie konvertieren möchten ( importFileTypeund importType), erhalten Sie einen Konverter aus der Liste der importierten Teile inconverters

var tmpConverter = (from x in converters
                    where x.Metadata.FileType == importFileType
                    && x.Metadata.ImportType == importType 
                    && (x.Metadata.CustomerUID == import.ImportDataCustomer.CustomerUID)
                    select x).OrderByDescending(x => x.Metadata.CustomerUID).FirstOrDefault();

if (tmpConverter != null)
{
     var converter = (IImportConverter)tmpConverter.Value;
     result = converter.ImportData(import.ImportDataFile, import.ImportDataFileName, importType);
....
}

Der Aufruf von converter.ImportDataverwendet den Code in der importierten Klasse.

Es mag wie eine Menge Code erscheinen und es kann eine Weile dauern, bis Sie wissen, was los ist, aber es ist äußerst flexibel, wenn Sie neue Konvertertypen hinzufügen, und Sie können sogar zur Laufzeit neue hinzufügen.

Matt
quelle
Ich habe noch nie von MEF gehört. Was ist es?
Jao
2
@jao check out link für eine vollständige Erklärung. Meiner Antwort wurden einige Beispiele für MEF hinzugefügt.
Matt
1
Dies ist eine hervorragende Möglichkeit, in die MEF einzusteigen. +1
paqogomez
MEF ist eine Technologie, kein Entwurfsmuster. Nein -1von mir, da die zugrunde liegende Idee immer noch Sinn macht und sich auf ein von der IImportConverterSchnittstelle vorgegebenes Strategiemuster stützt .
GETah
0

Was ist ein geeignetes Entwurfsmuster, um dieses Problem zu lösen?

Bei C # -Idiomen wird dazu das integrierte Serialisierungsframework verwendet. Sie beschriften die Objekte mit Metadaten und instanziieren dann verschiedene Serializer, die diese Beschriftungen verwenden, um Daten herauszureißen und in die richtige Form zu bringen, oder umgekehrt.

XML-, JSON- und Binärformulare sind am gebräuchlichsten, aber ich wäre nicht überrascht, wenn andere bereits in einer netten, verpackten Form existieren, die Sie verwenden können.

Telastyn
quelle
Das funktioniert gut, wenn Sie Ihr eigenes Dateiformat verwenden können, aber ich denke, dieser Ansatz schlägt bei komplexen, vordefinierten Formaten wie XSLX fehl, was bedeutet, dass MS Excel-Dateien im komprimierten XML-Format vorliegen.
Doc Brown
Ich kann eine Zeile einer Excel-Datei einem Objekt zuordnen, aber ich müsste diese Methode kopieren und an die XML- und CSV-Reader anpassen. Und ich möchte den Code so sauber wie möglich halten ...
Jao
@docBrown - wie? Konzeptionell unterscheidet sich die Umwandlung eines Objekts in eine Reihe von Zellen in Excel nicht wesentlich von der Umwandlung in ein XML-Dokument.
Telastyn
@Telastyn: Sie sagen, Sie können das eingebaute Serialisierungsframework des .NET-Frameworks zum Lesen des XLSX-Formats verwenden? In diesem Fall waren Bibliotheken wie das Open XML SDK oder NPOI veraltet.
Doc Brown
@docbrown: Entschuldigung, Sie haben Recht - ich vergesse immer wieder, dass es keine gemeinsame Serializer-Basisklasse gibt, da dies eines der ersten Dinge ist, die in einer Codebasis durchgeführt werden, in der ich arbeite.
Telastyn