我正在尝试开发一个模块,该模块将读取 excel 表(也可能来自其他数据源,因此它应该是松散耦合的)并将它们转换为实体以便保存。
逻辑将是这样的:
- Excel 工作表可以采用不同的格式,例如 Excel 工作表中的列名可以不同,因此我的系统需要能够将不同的字段映射到我的实体。
- 现在我将假设上面定义的格式是相同的并且现在是硬编码的,而不是在配置映射 UI 上设置之后动态地来自数据库。
- 数据甚至需要在映射之前进行验证。所以我应该能够事先针对某些东西进行验证。我们没有使用 XSD 或其他东西,所以我应该根据我用作导入模板的对象结构来验证它。
问题是,我把一些东西放在一起,但我并没有说我喜欢我所做的。我的问题是如何改进下面的代码并使事情更加模块化并解决验证问题。
下面的代码是一个模型,预计不会工作,只是为了看看设计的一些结构。
这是我到目前为止提出的代码,我已经意识到我需要提高我的设计模式技能但现在我需要你的帮助,如果你能帮助我:
//The Controller, a placeholder
class UploadController
{
//Somewhere here we call appropriate class and methods in order to convert
//excel sheet to dataset
}
在我们使用 MVC 控制器上传文件后,可能会有不同的控制器专门用于导入某些行为,在此示例中,我将上传与人员相关的表,
interface IDataImporter
{
void Import(DataSet dataset);
}
//我们可以使用除 PersonImporter 之外的许多其他导入器 class PersonImporter : IDataImporter { //我们将数据集划分为适当的数据表并调用所有与 Person 数据导入相关的 IImportActions //我们在这里调用 DataContext 的插入数据库函数,因为这样方式//我们可以做更少的数据库往返。
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];
}
}
我有IDataImporter
专门的具体课程PersonImporter
。但是,我不确定到目前为止它看起来是否良好,因为事情应该是 SOLID 所以基本上很容易在项目周期的后期扩展,这将成为未来改进的基础,让我们继续:
IImportActions
是魔法最常发生的地方。我不是基于表格设计事物,而是基于行为开发它,因此可以调用它们中的任何一个来以更模块化的模型导入事物。例如,一个表可能有 2 个不同的操作。
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();
}
}
}
还有验证,不幸的是,这主要是我最讨厌的地方。验证需要易于扩展和松耦合,而且我需要能够事先调用此验证而不是添加所有内容。
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;
}
}