LINQ To SQL - Going POCO and more…..!
Ok so I have posted on this a few times and rather than keep it waiting for the code to mature into anything deeper than more than what it is, I have decided to get the example working code up for download now and not keep it hanging around for too long.
A lot of people are expressing a lot of interest in the how to leverage LINQ To SQL using POCO (Plain Old CLR Objects) and this generic Repository approach utilises POCO’s along with Specifications and Fetching strategies (an nHibernate concept). The Repository<T> is designed for use out of the box however it can also be extended. I should make it clear that the code is not yet entirely complete and certainly I wouldn’t yet be releasing to my own team as ratified framework plumbing, nonetheless I am keen to get it posted to finish the discussion I started in previous posts.
The Cut and Thrust
The idea of the generic repository began as an extension of the ideas expressed by Mike Hadlow in his article here which are derived from Ayende’s work here.
public interface IRepository<T> { void Save(T domainObject); void SaveAll(IList<T> domainObjects); void SaveCompared(T modifiedDomainObject, T originalDomainObject); void SaveNew(T domainObject); void Delete(T domainObject); void Delete(ISpecification<T> spec); void Delete(T domainObject, Expression<Func<T, bool>> children); void Delete(Expression<Func<T, bool>> predicate); IList<T> All(); IList<T> All(ISpecification<T> spec); IList<T> All<TRole>(ISpecification<T> spec); IList<T> All(ISpecification<T> spec, IFetchingStrategy strategy); T First(); T First(ISpecification<T> spec); T First<TRole>(ISpecification<T> spec); T First(ISpecification<T> spec, IFetchingStrategy strategy); T Unique(ISpecification<T> spec); T Unique<TRole>(ISpecification<T> spec); T Unique(ISpecification<T> spec, IFetchingStrategy strategy); DataContext UnitOfWork { get; } }
Were the code really begins to diverge from Mike’s example is where you can see the dependencies that the IRepository has to the ISpecification and IFetchingStrategy, both of which I have posted previously about. ISpecification implements the Specification pattern popularised by Eric Evans in Domain Driven design and our ISpecification provides the ability to test for object equality by matching the specification ; the EvalPredicate is specifically targeted to enabling dynamic expression building and giving us dynamic querying in the process. The design of ISpecification is all but entirely based on Dave Laribee’s Sexy Specifications post, the addition of the EvalPredicate provides the dynamic query functionality.
public interface ISpecification<T> { Expression<Func<T, bool>> EvalPredicate { get; } bool Matches(T entity); }
The Fetching Strategy approach enables targeting code for runtime lazy or eager loading strategies and borrows from Udi’s approach with NHibernate and NServiceBus which is to allow for pluggable code that has no compile time coupling to the consumer code, that is, you can literally write Fetching Strategies and drop them in a folder location and if you call the write method in the Repository that accepts a generic paramater as a role or <TRole> [as a hint] then your strategy will be nice enough to load for you.
public interface IFetchingStrategy { DataLoadOptions LoadOptions { get; } } /// <summary> /// Marker Interface /// </summary> /// <typeparam name="TRole"> /// Specification of a role for /// dyanmic runtime type lookup /// </typeparam> public interface IFetchingStrategy<TRole> : IFetchingStrategy{ }
Some DDD’ers very much like to be very explicit with their Repositories in particular in defining the behaviours, such as GetByProductCatalog() for example. The generic approach is taken to reduce the amount of code written in the case where you may need many small repositories with little or no specific behaviours. Having said that you are still free to extend the generic repository and specialise that class.
Here are some samples that exemplify how to use the code to query your database.
public void SimpleRepositoryFetchingStrategyTest() { Specification<Customer> simonsCoSpec = new Specification<Customer>(c => c.Country == "Germany"); DataContext ctx = GetContext(); Repository<Customer> customerRepository = new Repository<Customer>(ctx); CustomerLoyaltyDiscountFetchingStrategy strategy = new CustomerLoyaltyDiscountFetchingStrategy(); var custs = customerRepository.All(simonsCoSpec, strategy); foreach (var cust in custs) { cust.LoyaltyDiscount(); Console.WriteLine("The Name of the Customer from " + "Germany is {0}", cust.CompanyName); foreach (var order in cust.Orders) { Console.WriteLine("\tOrder Number {0}", order.OrderID); foreach (var orderLine in order.OrderLines) { Console.WriteLine("\t\tProduct ID {0} Amount {1}", orderLine.ProductID, (orderLine.Quantity * orderLine.UnitPrice)); } } } }
As you can see our CustomerLoyaltyDiscountFetchingStrategy defines a lazy loading approach. It should be noted that Fetching Strategies can also include the use of the AssociateWith method of the DataLoadOptions class which is used to further refine the filtering capabilities of an IFetchingStrategy.
Another upside is that I can write unit tests that do not rely on my database and consequently slow down my testing automation. Ian Cooper demonstrated something similar and this a well known design goal amongst those that practice Domain Driven Design. You can see from the code example below, I am able to supply my repository with in memory objects and mock behaviours without any connection to the database.
[Test(Description="Tests the All method of the " + "Repository for accurate data Retrieval")] public void SingleExpectationFromAllMethod() { var mockRep = new Mock<IRepository<Customer>>(); mockRep.Expect(r => r.All()).Returns(() => new List<Customer>() { new Customer() { CustomerID = "SIMONS" } }); Assert.AreEqual(mockRep.Object.All().First().CustomerID, "SIMONS"); mockRep.VerifyAll(); }
So that’s it for this topic. Looks for the moment that I will use L2S where appropriate and although the Entity Framework is still pretty clunky and lacking purity in the POCO pursuits, I expect Microsoft to get it right eventually. You can download the entire code here which also includes Kris’ DebuggerWriter for printing SQL statements to the debug window from the LINQ To SQL DataContext and a slightly modified version of the ubiquitous Northwind database.



Great repository example - good to see the full code. Obviously a lot of work has gone in to this.
Do you use a service layer above the repository?
I am trying to get my head around how you would manage the data context in conjunction with your fetching strategies if a service method had to make multiple calls to the repository.
In the unit tests you create a new context for each call to the repository from what I can see e.g.
Repository orderRepos = new Repository(GetContext());
orderRepos.Delete(o => o.CustomerID == “SIMON”);
customerRepository = new Repository(GetContext());
customerRepository.Delete(cust);
scope.Complete();
Perhaps you inject a context factory into your service classes?
Also some explanation around the delete method with the children expression passed in would be great.
[Reply]
Dom
Yes you can use a Service Layer in tandem with the Repository. This approach doesn’t rely on an environment where the DataContext has continued state available, therefore the approach is to re-attach, hence your entity state (your POCO’s) should be maintained by your service (if it’s stateful or a long running service) or your ‘Model’ if your using MVP with ASP.Net, which is a classic example. I gather your question revolves around “what happens if I want to use more than one fetching strategy”? The DataContext is limited in this scenario. See ejr’s comment from a previous post in this thread of posts.
Thanks for pointing out the “children” argument in the delete method. The answer is that it is an unfinished idea and should be ignored, therefore I will update the post to highlight that.
Thanks,
Simon
[Reply]
This looks great but as with the other approaches to the repository model with L2S I am a bit confused. I have not seen any examples where you are actually joining tables together to run a query. All the examples seem to always be ‘get me the blue books’ type thing that return a list of the same type. This seems to imply that you have to run lots of queries and then join them together in memory in the service layer. What am I missing?!?!
[Reply]
depaulo
You should look into eager and lazy loading for L2S. Also the code download attached to this post (along with the update in http://www.simonsegal.net/blog/2008/09/27/linq-to-sql-going-poco-and-more-part-2/) demonstrate how to use Fetching Strategies. L2S by default will lazy load but you can use the System.Data.Linq.DataLoadOptions to specify what you wish to load and set the DataContext.DeferredLoadingEnabled to specifty lazy or otherwise. Also be aware that eager loading in L2S will look at the load options and choose the deepest level of loading associations so don’t assume that it will eager load everything in one query, always keep an eye on the sql trace our output the query from your code using the DataContext.Log property to specify an output stream.
[Reply]
Doesn’t the DataLoadOptions dependency limit you to a LINQ-to-SQL implementation of IRepository? Suppose I wanted an EF, wcf service, or in-memory implementation - none of my queries would be able to support DataLoadOptions!
[Reply]
Daniel
Yes your completely correct. At the time I must admit that I only saw this implementation as a LINQ To SQL specific one. I am working on changing that now.
[Reply]
I’m sorry, but this is just overly complicated, and not pleasant to use.
Your GetAll method should be returning IQueryable so that you can leverage LINQ - otherwise what’s the point?
If you leverage LINQ, you can get rid of about 90% of the rest of that interface.
[Reply]
Lemonhead
IList still gives you LINQ ‘leverage’ via extension methods. This repository is used in a continuously disconnected model (server side) with no concern for re-attachment of entities. IList leaves the implementation free of any orthogonal query provider concerns which have been abstracted by the Repository.
Your right about the ‘other’ methods such as First() and Unique() etc, however their intention is to provide an explicit approach that removed the need to filter .All().
How do you currently apply design to keep separation of concerns? What does your L2S DAL look like?
[Reply]