Importer une Feuille Excel et de Valider les Données Importées avec un couplage Lâche
Je suis en train de développer un module qui permettra de lire les feuilles de calcul excel (éventuellement à partir d'autres sources de données trop, de sorte qu'il devrait être faiblement couplés) et de les convertir dans d'autres Entités afin d'économiser.
La logique sera présent:
- La feuille excel peut être dans un format différent, par exemple les noms de colonne dans la feuille Excel peuvent être différents de mon système doit être en mesure de cartographier les différents champs de mes entités.
- Pour l'instant je vais être en supposant que le format défini ci-dessus sera même et codé en dur pour l'instant, au lieu de partir de la base de données dynamiquement après l'ensemble sur un mappage de configuration de l'INTERFACE utilisateur genre de chose.
- Les données doivent être validées avant même d'être substitués. Je devrais donc être en mesure de les valider à l'avance contre quelque chose. Nous ne sommes pas en utilisant comme XSD ou autre chose, alors je dois les valider par rapport à la structure de l'objet, je suis en utilisant comme modèle pour l'importation.
Le problème c'est que j'ai mis en place certaines choses ensemble, mais je ne dis pas que j'ai aimé ce que j'ai fait. Ma Question est comment je peux améliorer le code ci-dessous et rendre les choses plus modulaire et résoudre les problèmes de validation.
Le code ci-dessous est un mock-up, et n'est pas censé travailler, juste pour voir la structure de la conception.
C'est le code que j'ai trouvé jusqu'à présent, et j'ai réalisé une chose que je dois améliorer ma conception de modèles de compétences, mais pour l'instant j'ai besoin de votre aide, si vous pouviez m'aider:
//The Controller, a placeholder
class UploadController
{
//Somewhere here we call appropriate class and methods in order to convert
//excel sheet to dataset
}
Après nous fichier téléchargé à l'aide d'un Contrôleur MVC, il pourrait y avoir différents contrôleurs spécialisés à l'importation de certains comportements, dans cet exemple, je vais le téléchargement de personne de tables liées,
interface IDataImporter
{
void Import(DataSet dataset);
}
//On peut utiliser de nombreux autres importateurs d'ailleurs PersonImporter
classe PersonImporter : IDataImporter
{
//On divise l'ensemble de données approprate tableaux de données et d'appeler tous les IImportActions
//liées à la Personne de l'importation de données
//Nous appelons l'insertion de fonctions de base de données d'ici le DataContext depuis cette manière
//on peut faire moins de db aller-retour.
public string PersonTableName {get;set;}
public string DemographicsTableName {get;set;}
public Import(Dataset dataset)
{
CreatePerson();
CreateDemograhics();
}
//We put different things in different methods to clear the field. High cohesion.
private void CreatePerson(DataSet dataset)
{
var personDataTable = GetDataTable(dataset,PersonTableName);
IImportAction addOrUpdatePerson = new AddOrUpdatePerson();
addOrUpdatePerson.MapEntity(personDataTable);
}
private void CreateDemograhics(DataSet dataset)
{
var demographicsDataTable = GetDataTable(dataset,DemographicsTableName);
IImportAction demoAction = new AddOrUpdateDemographic(demographicsDataTable);
demoAction.MapEntity();
}
private DataTable GetDataTable(DataSet dataset, string tableName)
{
return dataset.Tables[tableName];
}
}
J'ai IDataImporter
et spécialisés de la classe de béton PersonImporter
. Cependant, je ne suis pas sûr que ça semble bon jusqu'ici puisque les choses devraient être SOLIDE donc, fondamentalement, facile à étendre plus tard dans le cycle du projet, ce sera une base pour de futures améliorations, permet de continuer:
IImportActions
sont où la magie se produit principalement. Au lieu de concevoir les choses de la table de base, je suis le développement de ce comportement en fonction de sorte qu'on peut appeler l'un d'eux à l'importation de choses en plus de modèle modulaire. Par exemple, une table peut avoir 2 actions différentes.
interface IImportAction
{
void MapEntity(DataTable table);
}
//A sample import action, AddOrUpdatePerson
class AddOrUpdatePerson : IImportAction
{
//Consider using default values as well?
public string FirstName {get;set;}
public string LastName {get;set;}
public string EmployeeId {get;set;}
public string Email {get;set;}
public void MapEntity(DataTable table)
{
//Each action is producing its own data context since they use
//different actions.
using(var dataContext = new DataContext())
{
foreach(DataRow row in table.Rows)
{
if(!emailValidate(row[Email]))
{
LoggingService.LogWarning(emailValidate.ValidationMessage);
}
var person = new Person(){
FirstName = row[FirstName],
LastName = row[LastName],
EmployeeId = row[EmployeeId],
Email = row[Email]
};
dataContext.SaveObject(person);
}
dataContext.SaveChangesToDatabase();
}
}
}
class AddOrUpdateDemographic: IImportAction
{
static string Name {get;set;}
static string EmployeeId {get;set;}
//So here for example, we will need to save dataContext first before passing it in
//to get the PersonId from Person (we're assuming that we need PersonId for Demograhics)
public void MapEntity(DataTable table)
{
using(var dataContext = new DataCOntext())
{
foreach(DataRow row in table.Rows)
{
var demograhic = new Demographic(){
Name = row[Name],
PersonId = dataContext.People.First(t => t.EmployeeId = int.Parse(row["EmpId"]))
};
dataContext.SaveObject(person);
}
dataContext.SaveChangesToDatabase();
}
}
}
Et de la validation, qui la plupart du temps où je suce à malheureusement. La validation doit être facile à étendre et faiblement couplés et aussi j'ai besoin d'être en mesure d'appeler cette validation à l'avance au lieu d'ajouter de tout.
public static class ValidationFactory
{
public static Lazy<IFieldValidation> PhoneValidation = new Lazy<IFieldValidation>(()=>new PhoneNumberValidation());
public static Lazy<IFieldValidation> EmailValidation = new Lazy<IFieldValidation>(()=>new EmailValidation());
//etc.
}
interface IFieldValidation
{
string ValidationMesage{get;set;}
bool Validate(object value);
}
class PhoneNumberValidation : IFieldValidation
{
public string ValidationMesage{get;set;}
public bool Validate(object value)
{
var validated = true; //lets say...
var innerValue = (string) value;
//validate innerValue using Regex or something
//if validation fails, then set ValidationMessage propert for logging.
return validated;
}
}
class EmailValidation : IFieldValidation
{
public string ValidationMesage{get;set;}
public bool Validate(object value)
{
var validated = true; //lets say...
var innerValue = (string) value;
//validate innerValue using Regex or something
//if validation fails, then set ValidationMessage propert for logging.
return validated;
}
}
Vous devez vous connecter pour publier un commentaire.
J'ai fait la même chose sur un projet. La différence est que je n'ai pas d'importer des feuilles de calcul Excel, mais les fichiers CSV. J'ai créé un CSVValueProvider. Et, par conséquent, les données au format CSV, était lié à mon IEnumerable modèle automatiquement.
Comme pour la validation, j'ai pensé que le fait d'aller à travers toutes les lignes et les cellules, et de valider un par un n'est pas très efficace, surtout lorsque le fichier CSV a des milliers de dossiers. Donc, ce que j'ai fait est que j'ai créé certaines méthodes de validation qui est allé à travers les données au format CSV, colonne par colonne, au lieu de, ligne par ligne, et a fait une requête linq sur chaque colonne et retourné les numéros de ligne des cellules avec des données non valides. Puis, il a ajouté l'invalide numéro de ligne/colonne des noms de ModelState.
Mise à JOUR:
Voici ce que j'ai fait...
CSVReader Classe:
CSVValueProviderFactory Classe:
CSVValueProvider Classe:
Pour la validation, comme je l'ai mentionné avant, j'ai pensé qu'il ne serait pas efficace de le faire en utilisant DataAnnotation attributs. Ligne par ligne de la validation des données peut prendre beaucoup de temps pour les fichiers CSV avec des milliers de lignes. Alors, j'ai décidé de valider les données dans le Contrôleur d'après le Modèle de Liaison est fait. Je devrais aussi mentionner que j'avais besoin de valider les données dans le fichier CSV à l'encontre de certaines données dans la base de données. Si vous avez juste besoin de valider des choses comme l'Adresse Email ou Numéro de Téléphone, vous pourriez tout aussi bien utiliser DataAnnotation.
Voici un exemple de méthode pour la validation de l'Adresse e-Mail colonne:
Mise à JOUR 2:
Pour validation à l'aide de DataAnnotaion, il vous suffit d'utiliser DataAnnotation attributs dans votre CSVViewModel comme ci-dessous (les CSVViewModel est la classe que vos données CSV sera lié à votre Contrôleur de l'Action):
Update 2
dans la réponse.