- I like the idea of persistence ignorance, clean ordinary classes where you focus on the business problem. I'm not too dogmatic about that. As an example I don't care about the LINQ To SQL mapping attributes I have to put in my domain classes.
- I don't want to run most of my unit tests against the database. The Repository pattern helps a lot in achieving this.
The central object of LINQ to SQL is the DataContext object. It tracks changes to all retrieved entities. It implements the Unit of Work and the Identity Map patterns and also provides query functionality on a per table basis. It's similar to NHibernate's Session object. Too bad that Microsoft didn't define an interface for this class (like the NHibernate team did it with the ISession interface). Such an interface is import to provide a stubbed implemenation during unit testing. So let's define our own interface and name it IDataContext:
public interface IDataContext: IDisposable
{
void Commit();
void DeleteOnSubmit<T>(T entity) where T: class;
ChangeSet GetChanges();
IQueryable<T> GetTable<T>() where T: class;
IQueryable<T> GetTable<T>(Expression<Func<T, bool>> predicate) where T: class;
void InsertOnSubmit<T>(T entity) where T: class;
}
The class that implements this interface is just an adapter for the DataContext class. The code is straight forward:
public class LinqToSqlDataContextAdapter: IDataContext
{
private readonly DataContext _dataContext;
private bool _disposed;
public LinqToSqlDataContextAdapter(IDbConnectionConfiguration connectionConfiguration): this(new DataContext(connectionConfiguration.ConnectionString))
{
}
protected LinqToSqlDataContextAdapter(DataContext dataContext)
{
_dataContext = dataContext;
}
public void Commit()
{
_dataContext.SubmitChanges();
}
public void DeleteOnSubmit<T>(T entity) where T: class
{
_dataContext.GetTable<T>().DeleteOnSubmit(entity);
}
//... more adapter code
}
Let's continue with the Repository Pattern. According to Fowler a Repository "provides a layer of abstraction over the mapping layer where query construction code is concentrated", to "minimize duplicate query logic". A Repository usually provides a set of query operations for an Entity. In addition to that objects can be added to and removed from the Repository. My interface for a generic Repository looks like this:
public interface IRepository<T> where T: IGuidIdentityPersistence
{
void Add(T entity);
long Count();
long Count(Expression<Func<T, bool>> predicate);
void Delete(T entity);
bool Exists();
bool Exists(Expression<Func<T, bool>> predicate);
T FindFirst(Expression<Func<T, bool>> predicate);
T Find(object id);
IQueryable<T> FindAll();
IQueryable<T> FindAll(Expression<Func<T, bool>> predicate);
}
I decided to use IQuerable
The generic implementation of IRepository<T> goes here:
public class Repository<T>: IRepository<T> where T: class, IGuidIdentityPersistence
{
private readonly IDataContext _dataContext;
public Repository(IDataContext dataContext)
{
_dataContext = dataContext;
}
public Repository()
{
_dataContext = UnitOfWork.Current;
}
private IDataContext DataContext
{
get { return _dataContext; }
}
public void Add(T entity)
{
DataContext.InsertOnSubmit(entity);
}
public long Count()
{
return DataContext.GetTable<T>().Count();
}
public long Count(Expression<Func<T, bool>> predicate)
{
return DataContext.GetTable(predicate).Count();
}
public void Delete(T entity)
{
DataContext.DeleteOnSubmit(entity);
}
public bool Exists()
{
return DataContext.GetTable<T>().Count() > 0;
}
public bool Exists(Expression<Func<T, bool>> predicate)
{
return DataContext.GetTable(predicate).Count() > 0;
}
public T FindFirst(Expression<Func<T, bool>> predicate)
{
return FindAll(predicate).FirstOrDefault();
}
public T Find(object id)
{
return DataContext.GetTable<T>().Where(e => e.Id.Equals(id)).FirstOrDefault();
}
/// <summary>
/// Returns a query for all object in the table for type T
/// </summary>
public IQueryable<T> FindAll()
{
return DataContext.GetTable<T>();
}
/// <summary>
/// Returns a query for all object in the table for type T that macht the predicate
/// </summary>
public IQueryable<T> FindAll(Expression<Func<T, bool>> predicate)
{
return DataContext.GetTable<T>(predicate);
}
}
I made the class concrete on propose. As the Repository class already defines a lot of helpful methods the class can be used in situations where you don't need a custom Repository. Below is an example where a generic Repository is used in a ASP.NET MVC Controller:
public class BookController: Controller
{
private IRepository<Location> _locationRepository;
public BookController(IRepository<Location> locationRepository)
{
_locationRepository = locationRepository;
}
//...
[AcceptVerbs("Post")]
public ActionResult Edit(Guid id, string title, string author, Guid locationId)
{
Book book = GetBook(id);
UpdateModel(book, new string[] {"Title", "Author"});
Location selectedLocation = _locationRepository.Find(locationId);
book.AddLocation(selectedLocation, GetCurrentUserName());
UnitOfWork.Current.Commit();
return View("Show", book);
}
A custom Repository could look like this:
public class BookRepository: Repository<Book>, IBookRepository
{
public BookRepository(IDataContext dataContext)
: base(dataContext)
{}
public BookRepository()
{}
public IEnumerable<Book> FindByTitle(string title)
{
return FindAll().Where(b => b.Title.Contains(title)).ToList();
}
}
The implemenation of the Repository pattern I showed in this post adds a layer of abstraction on top of LINQ to SQL and results in a more decoupled architecture. As a side effect the design simplifies database independent testing. The generic Repository provides easy and flexible usage for simpler situations.