Modello di progettazione per l'importazione di dati di vari tipi di origine e in vari tipi di destinazione


14

Devo progettare e costruire uno script di importazione (in C #) in grado di gestire quanto segue:

  • leggere dati da varie fonti (XML, XSLX, CSV)
  • verificare i dati
  • scrivere i dati su vari tipi di oggetti (cliente, indirizzo)

I dati provengono da diverse fonti ma una fonte avrà sempre un formato di importazione (CSV, XML, XLSX). I formati di importazione possono variare da fonte a fonte. Nuovi formati di importazione potrebbero essere aggiunti in futuro. I tipi di oggetto di destinazione sono sempre gli stessi (cliente, indirizzo e alcuni altri).

Ho pensato di usare i farmaci generici e ho letto qualcosa sul modello di fabbrica, ma in questo settore sono un grande sostenitore, quindi ogni consiglio è più che benvenuto.

Qual è un modello di progettazione appropriato per risolvere questo problema?


Mantienilo semplice.
NoChance,

Risposte:


11

Stai esagerando con concetti elaborati era troppo presto. Generici: quando vedi un caso li usi, ma altrimenti non preoccuparti. Modello di fabbrica - troppa flessibilità (e maggiore confusione) per questo ancora.

Mantienilo semplice. Usa le pratiche fondamentali.

  1. Prova a immaginare le cose comuni tra fare una lettura per XML, una lettura per CSV qualunque. Cose come il prossimo record, la prossima riga. Poiché è possibile aggiungere nuovi formati, provare a immaginare la comunanza che il formato da determinare avrebbe con quelli noti. Usa questa comunanza e definisci un '"interfaccia" o un contratto a cui devono aderire tutti i formati. Sebbene aderiscano al terreno comune, tutti possono avere le loro regole interne specifiche.

  2. Per convalidare i dati, provare a fornire un modo per collegare facilmente blocchi di codice validatore nuovi o diversi. Quindi, prova a definire un'interfaccia in cui ciascun validatore, responsabile di un particolare tipo di costruzione di dati, aderisca a un contratto.

  3. Per creare le costruzioni di dati probabilmente sarai costretto da chiunque progetta gli oggetti di output suggeriti più di ogni altra cosa. Prova a capire qual è il prossimo passo per gli oggetti dati e ci sono delle ottimizzazioni che puoi fare conoscendo l'uso finale. Ad esempio, se sai che gli oggetti verranno utilizzati in un'applicazione interattiva, potresti aiutare lo sviluppatore di quell'app fornendo "somme" o conteggi degli oggetti o altri tipi di informazioni derivate.

Direi che la maggior parte di questi sono modelli di modelli o modelli di strategia. L'intero progetto sarebbe un modello adattatore.


+1, specialmente per il primo paragrafo (ed è bello vedere che sei arrivato alla mia stessa conclusione nell'ultimo paragrafo).
Doc Brown,

Ricorda anche l'architettura dell'intero progetto, per adattare un formato all'altro. Riesci a immaginare una situazione in cui qualcuno potrebbe usarne solo una parte in un altro progetto? Ad esempio, forse un nuovo validatore di dati arriva sul mercato e funziona solo con SQL Server. Quindi ora vuoi solo leggere l'XML personalizzato e inserire il server SQL, saltando il resto dei passaggi.
Andyz Smith,

Per facilitare ciò, non solo i pezzi dovrebbero avere i loro contratti interni a cui aderiscono, ma ci dovrebbe essere una serie di contratti che definiscono l'interazione tra i pezzi .
Andyz Smith,

@AndyzSmith - Ho lo stesso problema nel mio codice. Ho capito tutto sul tuo codice tranne il modello Adapter. Quando hai detto che l'intero progetto è un esempio di modello di adattatore, puoi illustrarlo?
Gansub,

9

La cosa ovvia è applicare il modello di strategia . Avere una classe base generica ReadStrategye per ogni formato di input una sottoclasse come XmlReadStrategy, CSVReadStrategyecc. Ciò consentirà di modificare l'elaborazione di importazione indipendentemente dall'elaborazione della verifica e dall'elaborazione dell'output.

A seconda dei dettagli, può anche essere possibile mantenere la maggior parte delle parti di importazione generiche e scambiare solo parti dell'elaborazione di input (ad esempio, la lettura di un record). Ciò può condurre al modello Metodo modello .


Significa che quando si utilizza il modello di strategia, devo creare metodi separati per convertire gli oggetti (cliente, indirizzo) dalla fonte alla destinazione. Quello che vorrei fare è leggere, convertire e convalidare ogni oggetto e inserirlo in un elenco in modo che l'elenco possa essere successivamente salvato nel database.
jao,

@jao: bene, se leggi di nuovo la mia risposta, vedi che il mio suggerimento era di creare "ReadStrategy", non un "ConvertStrategy". Quindi devi solo scrivere metodi diversi per leggere gli oggetti (o qualunque altra parte aggiuntiva del tuo processo sia individuale per il formato file specifico).
Doc Brown,

7

Un modello adatto per un'utilità di importazione che potrebbe essere necessario estendere in futuro potrebbe essere l'utilizzo di MEF: è possibile mantenere basso l'utilizzo della memoria caricando il convertitore necessario al volo da un elenco pigro, creare importazioni MEF decorate con attributi che aiutano a selezionare il convertitore giusto per l'importazione che si sta tentando di eseguire e fornisce un modo semplice per separare le diverse classi di importazione.

Ogni parte MEF può essere costruita per soddisfare un'interfaccia di importazione con alcuni metodi standard che convertono una riga del file di importazione nei dati di output o sovrascrivono una classe base con la funzionalità di base.

MEF è un framework per la creazione di un'architettura plug-in: è il modo in cui sono costruiti Outlook e Visual Studio, tutte quelle belle estensioni in VS sono parti MEF.

Per creare un'app MEF (Managed Extensability Framework) iniziare con l'inclusione di un riferimento a System.ComponentModel.Composition

Definire le interfacce per specificare cosa farà il convertitore

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

Questo può essere utilizzato per tutti i tipi di file che si desidera importare.

Aggiungi attributi a una nuova classe che definisca ciò che la classe "Esporterà"

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

Ciò definirebbe una classe che importerà i file CSV (di un particolare formato: Format1) e ha attributi personalizzati che impostano i metadati degli attributi di esportazione MEF. Lo ripeteresti per ogni formato o tipo di file che desideri importare. Puoi impostare attributi personalizzati con una classe come:

[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; }
}

Per utilizzare effettivamente i convertitori MEF è necessario importare le parti MEF create durante l'esecuzione del codice di conversione:

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

catalog raccoglie le parti da una cartella, l'impostazione predefinita è la posizione dell'app.

converters è un elenco pigro delle parti MEF importate

Quindi, quando si conosce il tipo di file che si desidera convertire ( importFileTypee importType) ottenere un convertitore dall'elenco delle parti importateconverters

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);
....
}

La chiamata a converter.ImportDatautilizzerà il codice nella classe importata.

Potrebbe sembrare un sacco di codice e può volerci un po 'per capovolgere quello che sta succedendo, ma è estremamente flessibile quando si tratta di aggiungere nuovi tipi di convertitore e può anche permetterti di aggiungerne di nuovi durante il runtime.


Non ho mai sentito parlare di MEF prima. Che cos'è?
jao,

2
@jao controlla il link per una spiegazione completa. Aggiunti alcuni elementi MEF alla mia risposta.
Matt,

1
Questo è un modo eccellente per dare il via a MEF. +1
paqogomez,

MEF è una tecnologia, non un modello di progettazione. No -1da parte mia poiché l'idea di base ha ancora senso e si basa su un modello di strategia governato IImportConverterdall'interfaccia.
GETah il

0

Qual è un modello di progettazione appropriato per risolvere questo problema?

I linguaggi C # implicano l'uso del framework di serializzazione incorporato per fare ciò. Annotare gli oggetti con metadati e quindi creare un'istanza di serializzatori diversi che utilizzano tali annotazioni per estrarre i dati da inserire nella forma corretta o viceversa.

Xml, JSON e moduli binari sono i più comuni, ma non sarei sorpreso se altri esistessero già in un bel formato confezionato da consumare.


Bene, questo funziona bene se sei libero di usare il tuo formato di file, ma immagino che questo approccio fallirà per formati complessi e predefiniti come XSLX, il che significa file MS Excel in formato XML compresso.
Doc Brown,

Posso mappare una linea di un file Excel su un oggetto, ma avrei bisogno di copiare e adattare quel metodo ai lettori XML e CSV. E vorrei mantenere il codice il più pulito possibile ...
jao,

@docBrown - howso? Concettualmente, trasformare un oggetto in una serie di celle in Excel non è affatto diverso dal trasformarlo in un documento XML.
Telastyn,

@Telastyn: dici di poter usare il framework di serializzazione integrato del framework .NET per leggere il formato XLSX? Se ciò fosse vero, le librerie come Open XML SDK o NPOI erano obsolete.
Doc Brown,

@docbrown: le mie scuse, hai ragione - continuo a dimenticare che non esiste una classe base serializzatore comune poiché questa è una delle prime cose che viene fatta in qualsiasi base di codice in cui lavoro.
Telastyn
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.