VB.NET - there are times when you have to keep an open mind

I suddenly found myself needing to write a very simple upload method in our MVC controller.  The uploaded file is in CSV format.

The question that immediately jumps to mind is, where do I find a CSV parser?

  • Do I write my own - it may sound simple, but as soon as you start to think about all the various permutations of splitting a line of text, with all the escape characters as well as error handling, actually, it is NOT simple at all
    1. How do you handle the case where the user actually wants to use comma (,) in a field
    2. Do you include or exclude header row
    3. How do you handle line numbers and parse error
    4. What do you do with extra spaces or tabs
    5. How do you handle line breaks
    6. What about lines that didn't have matching number of fields
  • Do I then, just re-use someone else's example
  • Or do I use the one that's supplied and supported by Microsoft, and already installed on your machine as part of the .NET Framework since .NET 2.0?

The answer is blindingly obvious - use Microsoft's one - move on, next problem.

  • Well, it's in Microsoft.VisualBasic.FileIO

Hmm, let me think about this again…

 

This is where I find many C# developers suffer, we would rather build our own untested, unsupported code to do CSV parsing, either with String.Split(',') or with clever Regex.  We're convinced that our own code is better somehow than the Microsoft supplied library.  We're so convinced, we'll run off and spend 30minutes building our own parser, we'll follow by spending 2 hours on testing our parser and making sure it's bulletproof.

So I took a good look at myself and stopped.  I said to myself. 

No, I'll be pragmatic.

 

The Microsoft VB.NET CSV parser is part of the TextFieldParser class.  Documented here:

http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx

 

Through the Common Language Runtime (CLR) we can include Microsoft.VisualBasic library easily in our C# project.  Here they are, happily (may be) next to each other, and doing work for me.

image

 

The actual code is extremely simple:

using (TextFieldParser reader = new TextFieldParser(fileUpload.InputStream))
{
    reader.TextFieldType = FieldType.Delimited;
    reader.Delimiters = new[] { "," };

    while (!reader.EndOfData)
    {
        string[] parts = reader.ReadFields();

        if (parts.Length != 5)
        {
            // problem.
            continue;
        }
        string customerCode = parts[0];
        string customerName = parts[1];
        string contact = parts[2];
        string phone = parts[3];
        string information = parts[4];

        var customer = customers.FirstOrDefault(c => c.CustomerCode == customerCode);
        if (customer == null)
        {
            // new
            customer = new Customer()
            {
                CustomerCode = customerCode,
            };
            Entities.Customers.AddObject(customer);
        }

        customer.CustomerName = customerName;
        customer.CustomerPhone = phone;
        customer.CustomerContact = contact;
        customer.Information = information;
    }
    Entities.SaveChanges();
}