Search code examples
c#.netdatatableado.netdataview

.NET C# best object to transport data within application


For reasons of readability and better testability, I like to organize my app code into tiers - so I have multiple projects, each with multiple folder-structures, each folder with possibly multiple classes, with namespaces conforming to the topology. Nothing that special here - most developers structure code in a similar manner.

In my pure data tier, I need to execute a T-SQL Stored Procedure to retrieve a read-only dataset - I just need to iterate through the rows and read column values.

In another tier, I need to perform various data-transformations on the rowset ( example : compose a config file to be provided to another app ).

Normally in my data-tier class I'd use SqlDataReader to iterate the rows, extract the column values, and perform the data-transformation inline.

But I prefer to factor this functionality into a separate class.

So I need a disconnected data transport container. Possibilities are DataSet, DataTable, DataView, DataRowCollection - but perhaps these are overkill for my purposes.

What are some recommendations for the best class to transport read-only data ?


Solution

  • There are several different strategies that you could entertain to do this:

    1. Use LINQ to sql to avoid double iterating the list of rows (first pass to populate a datatable or list of pocos and second pass to transform)
    2. Define an IDataTransformer interface and pass that in to your data access load method and have the method use the transformer to shape the data while loading it from the SqlDataReader.
    3. Use Entity Framework with custom queries.

    There are a lot of resources for options 1 and 3 so I'll tackle option 2 here.

    Let's say you define an ISomethingDataAccess interface with the following method:

    public interface ISomethingDataAccess
    {
        List<T> LoadAll<T>(Func<IDataReader, T> transformer);
    }
    

    The transformer parameter will accept arguments containing a callback that will accept an IDataReader and return an instance of type T.

    The LoadAll<T> method is a generic method that allows the caller to determine the type of the objects that will be created from the IDataReader and then populated into the List<T> that will be returned from the method.

    In your implementation you do something like this:

    public class SqlSomethingDataAccess : ISomethingDataAccess
    {
        public List<T> LoadAll<T>(Func<IDataReader, T> transformer)
        {
            // ... Setup connection and command, etc ...
            var returnList = new List<T>();
            var reader = command.ExecuteReader();  // <- from setup steps above
            while(!reader.MoveNext())
            {
                returnList.Add(transformer(reader));
            }
            return returnList;
        }
    }
    

    Customize the method to your needs. Now you've separated the transformation concerns outside of the data access class.

    Use it something like this:

    public class SomethingBusiness()
    {
        public List<SomeTransformedThing> LoadAllSomethingsAsTransformedSomethings()
        {
            var returnList = this.dataAccess.LoadAll<SomeTransformedThing>
            (reader=>new SomeTransformedThing()
            {
                 // ... transform data from reader here
            });
    
            return returnList;
        }
    }