Linq Repositories
Recently, the beta of Linq to LLBLGen Pro was announced, which adds linq-querying capabilities to LLBLGen, the Object Relational Mapper that I use in most of my projects.
LLBLGen's querying API is very powerful, but also somewhat complex. For example, to retrieve a list of orders from all customers in the Northwind database for customers in the UK, you would write something like this:
EntityCollection<OrderEntity> orders = new EntityCollection<OrderEntity>(new OrderEntityFactory()); RelationPredicateBucket bucket = new RelationPredicateBucket(CustomerFields.Country == "UK") bucket.Relations.Add(OrderEntity.Relations.CustomerEntityUsingCustomerId) using(DataAccessAdapter adapter = new DataAccessAdapter()) { adapter.FetchEntityCollection(orders, bucket) }
Linq support maintains the type safety, whilst also allowing this query to be expressed in a more SQL-like fashion (which I personally find to be more intuitive):
using(DataAccessAdapter adapter = new DataAccessAdapter()) { var meta = new LinqMetaData(adapter); var query = from o in meta.Order where o.Customer.Country == "UK" select o; var results = query.ToList(); }
This got me thinking...now that there are several ORMs that have linq support, wouldn't it be nice if there was a consistent method for performing linq-based queries, independent of the underlying ORM implementation. And thus the linq-repository was born.
The majority of the work is done by the BaseRepository class which acts as a wrapper around the underlying linq provider. There's also an IRepository interface which the BaseRepository implements:
public interface IRepository<T> : IQueryable<T> where T : class { void Save(T toSave); void Save(T toSave, bool isNew); void Delete(T toDelete); } public abstract class BaseRepository<T> : IRepository<T> where T : class { private IQueryable<T> source; protected IQueryable<T> Source { get { return source; } set { source = value; } } protected BaseRepository(IQueryable<T> source) { this.source = source; } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return source.GetEnumerator(); } public IEnumerator GetEnumerator() { return source.GetEnumerator(); } public Expression Expression { get { return source.Expression; } } public Type ElementType { get { return source.ElementType; } } public IQueryProvider Provider { get { return source.Provider; } } public abstract void Save(T toSave); public abstract void Delete(T toDelete); public abstract void Save(T toSave, bool isNew);
Note that the constructor for BaseRepository takes an IQueryable representing the underlying linq provider. Now, the LLBLGen-specific subclass:
public class LLBLGenRepository<T> : BaseRepository<T> where T : EntityBase2, IEntity2, new() { public IDataAccessAdapter Adapter { get; private set; } public LLBLGenRepository(IDataAccessAdapter adapter, IElementCreator2 elementCreator) : base(CreateQuery(adapter, elementCreator)) { this.Adapter = adapter; } private static IQueryable CreateQuery(IDataAccessAdapter adapter, IElementCreator2 elementCreator) { return new DataSource2(adapter, elementCreator, functionMappings, null); } public override void Save(T toSave) { if (toSave == null) throw new ArgumentNullException("toSave"); Adapter.SaveEntity(toSave); } public override void Save(T toSave, bool isNew) { if (toSave == null) throw new ArgumentNullException("toSave"); if (isNew) toSave.IsNew = true; Adapter.SaveEntity(toSave); } public override void Delete(T toDelete) { Adapter.DeleteEntity(entity); }
Note that the constructor for the LLBLGenRepository takes instances of an IDataAccessAdapter and an IElementCreator (the two objects necessary for running linq-queries against LLBLGen) and creates a DataSource2 object (the LLBLGen query provider) which is then passed to the BaseRepository's constructor.
So, I can now write code like this:
IRepository<OrderEntity> orders = new LLBLGenRepository<OrderEntity>(new DataAccessAdapter(), new ElementCreator()); var query = from o in orders where o.Customer.Country == "UK" select o;
To remove the calls to new DataAccessAdapter() and new ElementCreator(), I moved the responsibility for instantiating the repository over to an IoC container:
//in my application startup routine: IoC.Initialise(new WindsorContainer()); IoC.Container.AddComponent<IDataAccessAdapter, DataAccessAdapter>(); IoC.Container.AddComponent<IElementCreator2, ElementCreator>(); IoC.Container.AddComponent("Repository", typeof(IRepository<>), typeof(LLBLGenRepository<>));
The IoC static class is simply a wrapper for the Windsor container.
Now I can instantiate repositories like this:
IRepository<OrderEntity> orders = IoC.Resolve<IRepository<OrderEntity>>();
I don't really like this, so I wrap it in a static gateway:
public static class Repository { public static IRepository<T&t; For<T>() where T : class, new() { return IoC.Resolve<IRepository<T>>(); } }
...and now I can write queries like this:
var query = from o in Repository.For<OrderEntity> where o.Customer.Country == "UK" select o;
Alternatively, now that the repository is registered with Windsor, I can inject the repository directly into my ASPNET MVC Controllers:
public class OrdersController : ConventionController { private IRepository<OrderEntity> ordersRepository; public OrdersController(IRepository<OrderEntity> repository) { this.ordersRepository = repository; } public void OrdersFromCustomersInTheUk() { ViewData["orders"] = from o in ordersRepository where o.Customer.Country == "UK" select o; } }