Search code examples
c#classoopdesign-patternsfilehelpers

Design pattern for grouping fields and structuring data in a flat file


I am using the FileHelpers C# library to read a file into an array of custom objects that will be processed. For instance, a partial file definition:

[FixedLengthRecord]
public class Row
{
    [FieldFixedLength(9)]
    [FieldTrim(TrimMode.Right)]
    public string Ssn;

    [FieldFixedLength(15)]
    [FieldTrim(TrimMode.Right)]
    public string LastName;

    ...
}

I'm trying to abide by OOP principles while doing this and I've noticed that some fields have a natural grouping (e.g. SSN and ClientID, EmployerID and EmployerName, etc.), so I tried to break them up into separate classes (e.g. Client and Employer). But this seems problematic because some of the fields need to be shared across objects (e.g. the ClientID needs to know the associated EmployerID).

To further complicate things, I would like to add fields with the [FieldNotInFile] attribute to the class definition(s) because during processing, my application will query for database records that match a specific row field and populate the row's respective [FieldNotInFile]s (e.g. get client's FName from database based on SSN since it's not in the file).

How would you structure this? I kind of just want to keep the whole file row as one class, but it's approaching 75 fields, which seems ridiculous. Thoughts?


Solution

  • A FileHelpers class is just a way of defining the specification of a flat file using C# syntax.

    As such, the FileHelpers classes are an unusual type of C# class and you should not try to use accepted OOP principles. (There are many ways a FileHelpers class violates OOP principles, most obviously it requires you to use public fields instead of properties). FileHelpers should not have properties or methods beyond the ones used by the FileHelpers library.

    Think of the FileHelpers class as the 'specification' of your CSV format only. That should be its only role. Then if you need the records in a more 'normal' object, then map the results to something better:

    FileHelperEngine engine = new FileHelperEngine<FileHelpersOrder>(); 
    var records = engine.ReadFile("FileIn.txt");
    
    var niceOrders = records.Select(
        x => new NiceOrder() 
           { Number = x.Number,  
             Customer = x.Customer 
             // etc.
           });
    

    Where FileHelpersOrder is your CSV specification and the NiceOrder class would be a proper OOP class with properties, methods, etc. as necessary.