Living in the Tech Avalanche Generation

A practitioner’s introspective on technology
Archive for September 16th, 2008

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.

Share/Save/Bookmark

16 comments

Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia