Adding events to LINQtoCSV library


How to improve Developer experience by adding custom events to libraries

The User Experience that a library provides must be at least equal with is quality and speed. And frankly, CSVtoLINQ rocks on latest two, as I presented in previous articles Import CSV file and query it with LINQ and continuing in LINQ wonder world, but lacks a little on the User Experience(in our case Developer Experience)  by not having some events of starting, progress and ending of the parsing.

Especially important, while parsing huge files, is a confirmation for the user that something happens and (ideally) the point in which the processing is. That is why, thanks to Matt Perdeck for sharing the entire source of the library, I was able to improve it by adding events.

So, let’s see some code!

Modifications into LINQtoCSV library – CSVContext.cs

Important: All modifications will be made in the CsvContext class from LINQtoCSV namespace – the CSVContext.cs file.

First we’ll add the ReadStarted event to the library – it will fire when the reading of the CSV file has started.

// defining the delegate
public delegate void ReadStartedHandler(object sender, EventArgs e);
// here we define the event 
public event ReadStartedHandler ReadStarted;

// the call of the event processing
protected virtual void OnReadStarted() {
  if (ReadStarted != null) {
    // we use empty eventargs because nothing is needed on readstarted event, just the confirmation of parsing started
    ReadStarted(this, EventArgs.Empty);
  }
}

Similarly, we define the complete event:

public delegate void ReadCompletedHandler(object sender, EventArgs e);
public event ReadCompletedHandler ReadCompleted;

protected virtual void OnReadCompleted() {
    if (ReadCompleted != null) {
        ReadCompleted(this, EventArgs.Empty);
    }
}

For the progress we have to do a small addition by creating our own EventArgs type:

public class ProgressArgs : EventArgs {
    private int _Progress;
    private int _Max;
       
    public int MaxValue {
        set {
            _Max= value;
        }
        get {
            return this._Max;
        }
    }

    public int Progress {
        set {
            _Progress = value;
        }
        get {
            return this._Progress;
        }
    }
}

public delegate void ReadProgressHandler(object sender, ProgressArgs p);
public event ReadProgressHandler ReadProgress;

protected virtual void OnReadProgress(ProgressArgs p) {
    if (ReadProgress != null) {
        ReadProgress(this, p);
    }
}

As the events and event arguments were created, we have to insert their calls into the code, with specific line numbers (might differ a little at your implementation time, but that’s why I presented some of the surrounding code) from the CSVContext.cs:

(...)
#region Get line counting for progress 
int count = 0;
string line;
while ((line = stream.ReadLine()) != null) {
    count++;
}

ProgressArgs p = new ProgressArgs();
p.MaxValue = count; 
#endregion
(...)
(...)
AggregatedException ae =
    new AggregatedException(typeof(T).ToString(), fileName, fileDescription.MaximumNbrExceptions);

try {
    ReadStarted(this, EventArgs.Empty);
    bool firstRow = true;

    while (cs.ReadRow(ref row)) {
        p.Progress++;
        ReadProgress(this, p);
(...)

And of course, the ReadCompleted event:

(...)
finally {
    if (readingFile) {
        stream.Close();
    }

    ReadCompleted(this, EventArgs.Empty);
(...)

Modifications in your application

Now, in your application you have to register and create methods responding to the events:

CsvContext cc = new CsvContext();
// add event handlers
cc.ReadStarted += new CsvContext.ReadStartedHandler(cc_ReadStarted);
cc.ReadCompleted += new CsvContext.ReadCompletedHandler(cc_ReadCompleted);
cc.ReadProgress += new CsvContext.ReadProgressHandler(cc_ReadProgress);

While the ReadStarted and ReadCompleted events are trivial, I feel that ReadProgress deserves to be presented as code( here tsProgressBar is a System.Windows.Forms.ToolStripProgressBar):

void cc_ReadProgress(object sender, ProgressArgs e) {
    if (tsProgressBar.Maximum != e.MaxValue)
        tsProgressBar.Maximum = e.MaxValue;
    tsProgressBar.Value = e.Progress;
    this.Cursor = Cursors.Default;
}

That’s all folks – Enjoy the code!

Leave a comment

Your email address will not be published. Required fields are marked *