Linq Repositories
March 24, 2008 – 7:49 pm
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; } }
3 Responses
There is something that I don’t understand in using LINQ to SQL in Repository pattern :
Let’s say that I will create an assembly for each of the Repository providers (LINQ to SQL, MySQL, Oracle, WebService, XML files etc. etc.). I will define the IRepository interface in a common assembly (let’s call it ‘Interfaces’) that will be referenced by any Repository provider assembly. But where will the entity classes be placed? Since they are generated by Visual Studio or by SQL Metal they would live in the LINQ to SQL provider but they are required in the ‘Interfaces’ assembly so I have a circular problem.
How do I go about this?
I’d suggest that you keep your entities in a separate assembly to your provider. Something like this should work:
MyApp.Entities – contains the entities generated by VS/SqlMetal
MyApp.Interfaces – contains your generic IRepository
MyApp.LinqToSqlRepository – contains the linq to sql specific implementaton of IRepository.
Very good advice! I did just that after I saw Rob Connery’s sample app.