Excel シートを読み取り (おそらく他のデータ ソースからも、疎結合にする必要があります)、それらをエンティティに変換して保存するモジュールを開発しようとしています。
ロジックは次のようになります。
- Excel シートはさまざまな形式にすることができます。たとえば、Excel シートの列名が異なる可能性があるため、システムはさまざまなフィールドをエンティティにマップできる必要があります。
- 今のところ、構成マッピング UI のようなものに設定した後にデータベースから動的に取得するのではなく、上記で定義した形式が同じでハードコードされていると仮定します。
- マッピングする前に、データを検証する必要があります。したがって、何かに対して事前に検証できるはずです。XSD などを使用していないので、インポート用のテンプレートとして使用しているオブジェクト構造に対して検証する必要があります。
問題は、いくつかのことをまとめましたが、自分のやったことを気に入ったとは言えません。私の質問は、以下のコードを改善し、物事をよりモジュール化し、検証の問題を修正する方法です。
以下のコードはモックアップであり、設計の構造を確認するためだけに動作することは期待されていません。
これは私がこれまでに思いついたコードであり、デザイン パターンのスキルを向上させるために必要なことが 1 つありますが、今のところはあなたの助けが必要です。
//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 { //データセットを分割して適切なデータ テーブルを作成し、すべての IImportActions を呼び出します //Person データのインポートに関連する //DataContext のここでデータベースへの挿入関数を呼び出します。 way //db ラウンドトリップを減らすことができます。
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
ます。ただし、プロジェクトサイクルの後半で基本的に簡単に拡張できるようにする必要があるため、これまでのところ見栄えが良いかどうかはわかりません。これは将来の改善の基盤になります。続けましょう。
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;
}
}