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 ...

posted on Sunday, August 26, 2007 11:29 PM by scott

Comments

Sunday, August 26, 2007 9:46 PM by Thomas Eyde

# re: Trying Out Persistence Ignorance with LINQ (Part I)

This is an interesting twist on TDD and LINQ. The LINQ expressions themselves are impossible to mock, I have heard, but this approach seems very nice.

However, I have trouble to follow the code when the interface is 'IUnitOfWork', while the parameter is named 'context'.
Monday, August 27, 2007 6:08 AM by scott

# re: Trying Out Persistence Ignorance with LINQ (Part I)

Thomas:

I agree, and that is something I need to go back and change. The iterface itself went through several iterations of renaming and I never cleaned up the parameter names.
Monday, August 27, 2007 8:03 PM by Christopher Steen

# Link Listing - August 27, 2007

Link Listing - August 27, 2007
Tuesday, August 28, 2007 7:34 PM by K. Scott Allen

# Trying Out Persistence Ignorance with LINQ (Part II)

In Part I, we defined an IUnitOfWork interface to avoid coding directly to the System.Data.Linq.DataContext...
Tuesday, August 28, 2007 7:44 PM by BusinessRx Reading List

# Trying Out Persistence Ignorance with LINQ (Part II)

In Part I , we defined an IUnitOfWork interface to avoid coding directly to the System.Data.Linq.DataContext
Thursday, January 15, 2009 1:14 PM by Team Datalogic

# A better mock repository pattern

A better mock repository pattern
Thursday, January 15, 2009 1:15 PM by Team Datalogic

# A better mock repository pattern

A better mock repository pattern
Sunday, May 03, 2009 12:06 PM by DotNetNerds blog

# Repository design med LINQ

Repository design med LINQ
Sunday, May 03, 2009 12:06 PM by DotNetNerds blog

# Repository design med LINQ

Repository design med LINQ
Monday, June 08, 2009 2:13 PM by DotNetNerds blog

# Repository design med LINQ

Repository design med LINQ