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 ?
There are several different strategies that you could entertain to do this:
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;
}
}