I had to write one data import/export utility that moved data from one database to another. Once I was finished the first round of coding I found one thing that needed refactoring – the import/export process wasn’t generalized. It was part of importer/exporter utility main class (it was console application). After some refactoring I got some common things to use in other tasks like this.
My main idea was to extract interface for processes like this and then define some events. Let’s suppose we have process that reads invoices from one database and writes them after some data manipulation to other database. We receive list of invoices and then start processing them.
Process delegates
Every import/export process has start, processing part and finish. Processing part is step that is repeated for every data unit we are processing. All these three may be events that our processing class fires. Let’s define some delegates for these events.
delegate void ProcessStartDelegate(int count);
delegate void ProcessStepDelegate<T>(T obj, int index, int count);
delegate void ProcessFinishDelegate();
ProcessStartDelegate is fired when process starts. It is received input data and wants to start processing. Let’s say we have invoices as input data. As we have list of invoices we can say how many invoices we have. This is why ProcessStartDelegate has argument calles count.
ProcessStepDelegate is fired for every invoice we have. We want to use these delegates in different import/export processes, so we have to make ProcessStepDelegate generic because this way we can say type of object to it. Index is argument that shows the index of current invoice in input array. count is also there because some processes may change the input collection size during their work.
ProcessFinishDelegate is fired when process finishes. This delegate has no arguments because we already know how much elements we have.
Process interface
Now we have our delegates and we are ready to take the next step. Let’s define now interface for import/export processes. We want generic interface so we can use it also with other import/export classes. ProcessStepDelegate was generic and so we have to make our interface generic also. So here is our interface.
interface IProcess<T>
{
event ProcessStartDelegate ProcessStart;
event ProcessStepDelegate<T> ProcessStep;
event ProcessFinishDelegate ProcessFinish;
void Process();
}
I added also method Process() this interface because we need something we can call to start the process. This interface is all we need here.
Example
Let’s see now simple example about how to use IProcess interface. As I said before we suppose we have class for invoices. We call this class Invoice.
public class InvoiceImporter : IProcess<Invoice>
{
public event ProcessStartDelegate ProcessStart;
public event ProcessStepDelegate<Invoice> ProcessStep;
public event ProcessFinishDelegate ProcessFinish;
public InvoiceImporter() { }
public void Process()
{
List<Invoice> invoices = LoadInvoices();
if (this.ProcessStart != null)
this.ProcessStart(invoices.Count);
int i = 0;
foreach (Invoice invoice in invoices)
{
if (this.ProcessStep != null)
this.ProcessStep(invoice, ++i, invoices.Count);
ProcessInvoice(invoice);
}
if (this.ProcessFinish != null)
this.ProcessFinish();
}
private List<Invoice> LoadInvoices()
{
List<Invoice> invoices = new List<Invoice>();
// Fill list here
return invoices;
}
private void ProcessInvoice(Invoice invoice)
{
// Do some processing here
}
}
This is simple skeleton of invoice importer. I follows completely IProcess interface. You can use this interface for different kinds of import/export classes to make sure they all follow the same pattern.