Sunday 11 October 2009

Why LINQ to SQL sucks

Given the following repository interface how can you create a LINQ to SQL implementation that uses data transfer objects (POCO classes that are agnostic as to the underlying data access technology) when instantiating the repository?
public interface IRepository<T> where T : class 
{ 
    void Save(T instance); 

    void Delete(T instance); 

    TResult FindOne<TResult>(ISpecification<T, TResult> specification); 

    IQueryable<TResult> FindAll<TResult>(ISpecification<T, TResult> specification); 

    IQueryable<T> FindAll(); 
}
Your LINQ to SQL repository implementation might look something like this (I’ve snipped a bunch of code and left one method implemented):
public class Repository<T> : IRepository<T> where T : class 
{ 
    private readonly DataContext _ctx; 

    public Repository(DataContext ctx) 
    { 
        _ctx = ctx; 
    } 

    public void Save(T instance) ... 
    public void Delete(T instance) ...  
    public TResult FindOne<TResult>(ISpecification<T, TResult> specification) ... 
    public IQueryable<TResult> FindAll<TResult>(ISpecification<T, TResult> specification) ...

    public IQueryable<T> FindAll() 
    { 
        return _ctx.GetTable<T>().AsQueryable(); 
    } 
}
The problem is that the call to _ctx.GetTable<T>() expects T to be a type defined in the LINQ to SQL layer – not a data transfer type. 
Let’s say you have a User data transfer class. You would expect to instantiate the repository as follows:
var repository = new Repository<User>();
This won’t work because the User type would have to be the User type defined in the LINQ to SQL layer. Your client code will no longer be agnostic as to the data access layer.
How rubbish is that!
There are some very hacky workarounds that all involve writing lots of unnecessary code.
See:
Sunday 11 October 2009