Trying Out Persistence Ignorance with LINQ (Part I)
In June, Ian Cooper wrote an article (Being Ignorant With LINQ to SQL) that provided marvelous details about using POCOs with persistence ignorant repositories. Ian's conclusion: "LINQ to SQL is usable with a TDD/DDD approach to development. Indeed the ability to swap between LINQ to Objects and LINQ to SQL promises to make much more of the code easily testable via unit tests than before."
I've been tinkering along the same lines as Ian, and although I think I started working at the other end of the problem first, I'm reaching the same conclusions (and ended up with much of the same code).
I started working with a trivial case, thinking if the trivial case becomes difficult, the topic isn't worth pursuing. Let's suppose we have the following class.
public class FoodFact
{
public int ID
{
get { return _id; }
set { _id = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public int Calories
{
get { return _calories; }
set { _calories = value; }
}
int _id;
int _calories;
string _name;
}
The idea is that each FoodFact object will ultimately be persisted as a row in a SQL Server database, but I don't want to clutter up the FoodFact
class with unrelated attribute decorations or other database nonsense. To get into the
database, I do need some sort of gateway, or class that can track POCO objects
and determine if they need persisted. In LINQ, this class is the
System.Data.Linq.DataContext class. The DataContext class itself doesn't
implement any interesting interfaces, so my plan was to abstract the DataContext
in a a stubable, fakeable, mockable fashion.
public interface IUnitOfWork : IDisposable
{
IDataSource<T> GetDataSource<T>() where T : class;
void SubmitChanges();
}
We have not looked at IDataSource yet, but there will be at least two implementations of IUnitOfWork. One implementation will work against a real database using a persistent data source (where FoodDB is a class derived from DataContext):
public class UnitOfWork : IUnitOfWork
{
public UnitOfWork()
{
_context = FoodDB.Create();
}
public IDataSource<T> GetDataSource<T>() where T : class
{
return new PersistentDataSource<T>(_context);
}
public void SubmitChanges()
{
_context.SubmitChanges();
}
public void Dispose()
{
_context.Dispose();
}
FoodDB _context = null;
}
Another implementation will work with an "in memory data source".
This class would be defined in a unit test project.
class InMemoryUnitOfWork : IUnitOfWork
{
public IDataSource<T> GetDataSource<T>() where T : class
{
return new InMemoryDataStore<T>();
}
public void SubmitChanges()
{
}
public void Dispose()
{
}
}
The goal is to consume IUnitOfWork along these lines:
public class FoodFactRepository
: IRepository<FoodFact>
{
IDataSource<FoodFact> _dataSource;
IUnitOfWork _context;
public FoodFactRepository(IUnitOfWork context)
{
Check.ArgIsNotNull(context, "context");
_context = context;
_dataSource = _context.GetDataSource<FoodFact>();
Check.IsNotNull(_dataSource, "Could not retrieve a data source");
}
public FoodFact FindByID(int ID)
{
return
(
from fact in FoodFacts
where fact.ID == ID
select fact
).FirstOrDefault();
}
public FoodFact Add(FoodFact fact)
{
Check.ArgIsNotNull(fact, "fact");
_dataSource.Add(fact);
return fact;
}
public IDataSource<FoodFact> FoodFacts
{
get { return _dataSource; }
}
// ...
What are these data sources? Stay tuned for part II ...