Je dois concevoir et construire un script d'importation (en C #) qui peut gérer les éléments suivants:
Les données proviendront d'un certain nombre de sources mais une source aura toujours un format d'importation (soit csv, xml, xslx). Les formats d'importation peuvent varier d'une source à l'autre. De nouveaux formats d'importation pourraient être ajoutés à l'avenir. Les types d'objet de destination sont toujours les mêmes (client, adresses et quelques autres).
J'ai pensé à utiliser des génériques et j'ai lu quelque chose sur le modèle d'usine, mais je suis un assez grand noob dans ce domaine, donc tout conseil est le bienvenu.
Quel est un modèle de conception approprié pour résoudre ce problème?
Vous allez trop loin avec des concepts fantaisistes c'était trop tôt. Génériques - lorsque vous voyez un cas, utilisez-les, mais sinon, ne vous inquiétez pas. Modèle d'usine - beaucoup trop de flexibilité (et confusion supplémentaire) pour le moment.
Rester simple. Utilisez des pratiques fondamentales.
Essayez d'imaginer les choses courantes entre faire une lecture pour XML, une lecture pour CSV. Des choses comme, prochain enregistrement, prochaine ligne. Étant donné que de nouveaux formats peuvent être ajoutés, essayez d'imaginer les similitudes que le format à déterminer aurait avec les formats connus. Utilisez cette similitude et définissez une "interface" ou un contrat auquel tous les formats doivent adhérer. Bien qu'ils adhèrent au terrain d'entente, ils peuvent tous avoir leurs règles internes spécifiques.
Pour valider les données, essayez de fournir un moyen de connecter facilement des blocs de code de validation nouveaux ou différents. Encore une fois, essayez de définir une interface où chaque validateur, responsable d'un type particulier de construction de données, adhère à un contrat.
Pour créer les constructions de données, vous serez probablement contraint par celui qui conçoit les objets de sortie suggérés plus que tout. Essayez de comprendre quelle est la prochaine étape pour les objets de données, et y a-t-il des optimisations que vous pouvez faire en connaissant l'utilisation finale. Par exemple, si vous savez que les objets vont être utilisés dans une application interactive, vous pouvez aider le développeur de cette application en fournissant des "sommations" ou des dénombrements des objets ou d'autres types d'informations dérivées.
Je dirais que la plupart d'entre eux sont des modèles de modèle ou des modèles de stratégie. L'ensemble du projet serait un modèle d'adaptateur.
La chose évidente est d'appliquer modèle de stratégie . Avoir une classe de base générique ReadStrategy
et pour chaque format d'entrée une sous-classe comme XmlReadStrategy
, CSVReadStrategy
etc. Cela vous permettra de changer le traitement d'importation indépendamment du traitement de vérification et du traitement de sortie.
Selon les détails, il peut également être possible de conserver la plupart des parties du générique d'importation et d'échanger uniquement des parties du traitement d'entrée (par exemple, la lecture d'un enregistrement). Cela peut vous conduire à modèle de méthode de modèle .
Un modèle approprié pour un utilitaire d'importation que vous devrez peut-être étendre à l'avenir serait d'utiliser MEF - vous pouvez réduire l'utilisation de la mémoire en chargeant le convertisseur dont vous avez besoin à la volée à partir d'une liste paresseuse, créer des importations MEF décorées d'attributs qui aident à sélectionner le bon convertisseur pour l'importation que vous essayez d'effectuer et fournit un moyen facile de séparer les différentes classes d'importation.
Chaque partie MEF peut être construite pour satisfaire une interface d'importation avec certaines méthodes standard qui convertissent une ligne du fichier d'importation en vos données de sortie ou remplacent une classe de base avec la fonctionnalité de base.
MEF est un cadre pour créer une architecture de plug-in - sa façon dont Outlook et Visual Studio sont construits, toutes ces belles extensions dans VS sont des parties MEF.
Pour créer une application MEF (Managed Extensability Framework), commencez par inclure une référence à System.ComponentModel.Composition
Définir des interfaces pour spécifier ce que fera le convertisseur
public interface IImportConverter
{
int UserId { set; }
bool Validate(byte[] fileData, string fileName, ImportType importType);
ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}
Cela peut être utilisé pour tous les types de fichiers que vous souhaitez importer.
Ajouter des attributs à une nouvelle classe qui définissent ce que la classe "exportera"
[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
...interface methods...
}
Cela définirait une classe qui importera des fichiers CSV (d'un format particulier: Format1) et possède des attributs personnalisés qui définissent les métadonnées d'attribut d'exportation MEF. Vous répéteriez cela pour chaque format ou type de fichier que vous souhaitez importer. Vous pouvez définir des attributs personnalisés avec une classe comme:
[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; }
}
Pour utiliser réellement les convertisseurs MEF, vous devez importer les pièces MEF que vous créez lorsque votre code de conversion est exécuté:
[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();
catalog
recueille les pièces d'un dossier, par défaut est l'emplacement de l'application.
converters
est une liste paresseuse des pièces MEF importées
Ensuite, lorsque vous savez quel type de fichier vous souhaitez convertir (importFileType
et importType
), obtenez un convertisseur à partir de la liste des pièces importées dans converters
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);
....
}
L'appel à converter.ImportData
utilisera le code dans la classe importée.
Cela peut sembler beaucoup de code et cela peut prendre un certain temps pour comprendre ce qui se passe, mais c'est extrêmement flexible quand il s'agit d'ajouter de nouveaux types de convertisseur et peut même vous permettre d'en ajouter de nouveaux pendant l'exécution.
Quel est un modèle de conception approprié pour résoudre ce problème?
Les idiomes C # impliquent d'utiliser le cadre de sérialisation intégré pour ce faire. Vous annotez les objets avec des métadonnées, puis instanciez différents sérialiseurs qui utilisent ces annotations pour extraire les données à mettre sous la bonne forme, ou vice versa.
Les formes Xml, JSON et binaires sont les plus courantes, mais je ne serais pas surpris si d'autres existent déjà sous une forme packagée Nice à consommer.