A suitable pattern for an importing utility that you may need to extend in the future would be to use MEF - you can keep memory use low by loading the converter you need on the fly from a lazy list, create MEF imports that are decorated with attributes that help select the right converter for the import you are trying to perform and provides an easy way of separating the different importing classes out.
Each MEF part can be built to satisfy an importing interface with some standard methods that convert a row of the import file to your output data or override a base class with the basic functionality.
MEF is an framework for creating a plug-in architecture - its how outlook and Visual Studio are built, all those lovely extensions in VS are MEF parts.
To build a MEF (Managed Extensability Framework) app start with including a reference to System.ComponentModel.Composition
Define interfaces to spec out what the converter will do
public interface IImportConverter
{
int UserId { set; }
bool Validate(byte[] fileData, string fileName, ImportType importType);
ImportResult ImportData(byte[] fileData, string fileName, ImportType importType);
}
This can be used for all the file types you want to import.
Add attributes to a new class that define what the class will "Export"
[Export(typeof(IImportConverter))]
[MyImport(ImportType.Address, ImportFileType.CSV, "4eca4a5f-74e0")]
public class ImportCSVFormat1 : ImportCSV, IImportConverter
{
...interface methods...
}
This would define a class that will import CSV files (of a particular format : Format1) and has a custom attributes that sets MEF Export Attribute Metadata. You'd repeat this for each format or file type you want to import. You can set custom attributes with a class like:
[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; }
}
To actually use the MEF converters you need to import the MEF parts you create when your converting code is run :
[ImportMany(AllowRecomposition = true)]
protected internal Lazy<IImportConverter, IImportMetadata>[] converters { get; set; }
AggregateCatalog catalog = new AggregateCatalog();
catalog
collects the parts from a folder, default is app location.
converters
is a lazy list of the imported MEF parts
Then when you know what sort of file you want to convert (importFileType
and importType
) get a converter from the list of imported parts in 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);
....
}
The call to converter.ImportData
will use the code in the imported class.
Might seem like a lot of code and it can take a while to get your head round whats going on but its extremely flexible when it comes to adding new converter types and can even allow you to add new ones during runtime.