Saturday, October 25, 2008

TDD and LINQ to SQL

Microsoft has made the unfortunate mistake of not making System.Data.Linq.Table<T> implement an interface. If they had only done this it would have been easy to mock out the Persistence Layer when using LINQ to SQL.

Since it is a class and a sealed one at that, this is not an available path for TDD. What to do? If we can’t implement it we can adapt it. What is required for this to work is two interfaces: IUnitOfWork and ITable<T>, two classes LinqToSqlUnitOfWork and LinqToSqTable<T> and for testing purposes two additional classes InMemoryUnitOfWork and InMemoryTable<T>.

ITable<T> looks like this. I usually only include the methods that I need and add more by need.


   public interface ITable<T> : IQueryable<T> where T : class
    {
        void Attach(object entity);
        IQueryable<TElement> CreateQuery<TElement>(Expression expression);
        IQueryable CreateQuery(Expression expression);
        void DeleteAllOnSubmit(IEnumerable entities);
        void DeleteOnSubmit(T entity);
        object Execute(Expression expression);
        TResult Execute<TResult>(Expression expression);
        void InsertAllOnSubmit(IEnumerable entities);
        void InsertOnSubmit(T entity);
    }

IUnitOfWork looks like this with one accessor method for every aggregate in my domain model. I have only included SubmitChanges in the interface but it is entirely possible to include all the methods of DataContext here if you have the need for it.


   public interface IUnitOfWork
   {
       ITable<Player> Players { get; }
       ITable<Round> Rounds { get; }
       void SubmitChanges();
   }

LinqToSqlUnitOfWork looks like this. Every method wraps the sealed Table<T> in a LinqToSqlTable<T>.


public class LinqToSqlUnitOfWork : IUnitOfWork
{
     private readonly LinqToSqlDataContext dataContext;

     public LinqToSqlUnitOfWork(LinqToSqlDataContext dataContext)
     {
         this.dataContext = dataContext;
     }

     public ITable<Player> Players
     {
         get { return new LinqToSqlTable<Player>(dataContext.GetTable<Player>()); }
     }

     public ITable<Round> Rounds
     {
         get { return new LinqToSqlTable<Round>(dataContext.GetTable<Round>()); }
     }

     public void SubmitChanges()
     {
         dataContext.SubmitChanges();
     }

 }

The next class need is LinqToSqlTable<T>. As shown above the constructor takes a Table<T> as a parameter. All actual work is delegated to the Table<T>.


public class LinqToSqlTable<T> : ITable<T> where T : class
{
    private readonly Table<T> table;

    public LinqToSqlTable(Table<T> table)
    {
        this.table = table;
    }


    public void Attach(T entity)
    {
        table.Attach(entity);
    }

    public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
    {
        return ((IQueryProvider) table).CreateQuery<TElement>(expression);
    }

    public IQueryable CreateQuery(Expression expression)
    {
        return ((IQueryProvider) table).CreateQuery(expression);
    }

    public void DeleteOnSubmit(T entity)
    {
        table.DeleteOnSubmit(entity);
    }
    ...

Finally we have the InMemory classes. InMemoryUnitOfWork works similarly to SqlToLinqUnitOfWork creating InMemoryTables that implement the ITable<T> Interface. The SubmitChanges method is left as an interesting exercise for the reader :)


public class InMemoryUnitOfWork : IUnitOfWork
{
    private readonly ITable<Player> players;
    private readonly ITable<Round> rounds;

    public InMemoryUnitOfWork()
    {
        players = new InMemoryTable<Player>();
        rounds = new InMemoryTable<Round>();
    }

    public ITable<Player> Players
    {
        get { return players; }
    }

    public ITable<Round> Rounds
    {
        get { return rounds; }
    }

    public void SubmitChanges()
    {
        //TODO
    }
}

In its simplest form the InMemoryTable<T> just uses a List<T> as storage, delegating all possible functionality to the list. A lot of methods are left out for brevity.


    public class InMemoryTable<T> : ITable<T> where T : class 
    {
        private readonly IList<T> list;
 
        public InMemoryTable() : this(new List<T>())
        {
        }

        public InMemoryTable(IList<T> aList)
        {
            list = aList;
        }

        public IEnumerator<T> GetEnumerator()
        {
            return list.GetEnumerator();
        }

        public Expression Expression
        {
            get { return list.AsQueryable().Expression; }
        }
   ...


This simple wrapper gives me the ability to create repositories for my classes that work for both LingToSql and with in memory collections. Here is an example:


public class RoundRepository
 {
     private readonly IUnitOfWork unitOfWork;

     public RoundRepository(IUnitOfWork unitOfWork)
     {
         this.unitOfWork = unitOfWork;
     }

     public IQueryable<Round> Rounds
     {
         get { return unitOfWork.Rounds; }
     }

     public void AddRound(Round round)
     {
         unitOfWork.Rounds.InsertOnSubmit(round);
     }

     public void ClearRounds()
     {
         unitOfWork.Rounds.DeleteAllOnSubmit(unitOfWork.Rounds);
     }

     public IEnumerable<Round> ByYear(int year)
     {
         var rounds = from round in Rounds
                      where round.PlayDate.Year == year
                      select round;
         return rounds;
     }
 }


This gives me the ability to test my domain classes at the relatively small cost of six new classes and interfaces. It also allows me to use the same test code for both unit testing and integration testing. Not bad for a days work :)

Thursday, October 23, 2008

Code Comments

I am a firm believer of Programming by Intent and TDD as a means to communicate effectively with my fellow programmers (current and future). I find that sometimes this is not enough and I have therefore started using the following code comments to improve the communication.

TODO: why, why not

The TODO signals that here is incomplete functionality here. The comment should be followed by a reason why this should be done and why it isn't.

WTF: who doesn't get it, what

A WTF signals that there is something I don't understand. It is a signal to whomever wrote the code to clarify its intent by refactoring or with a BECAUSE comment. Hopefully this code is written by someone else. :) The WTF should be followed by my signature and what I don't get.

BECAUSE: who, why

A BECAUSE signals that I am unable to express my intent in code and that I need help clarifying it. The comment contains my signature and what I am trying to express. Hopefully I or someone else will be able to clarify my intent later.

REF: link

A REF signals that this code is described elsewhere. The code may be an algorithm that is described somewhere on the web or in a book. The ref is followed by a URL or some other pointer that allows whomever is interested to find the information easily. I usually add the comment patterns as to-do regexps in my editor making it easy to navigate to them. I also let the continuous integration server parse for them to give me a list of things that needs attention.

The comments also provide the additional value of showing the quality of the code.

Wednesday, October 01, 2008

Notes on 'Secrets of Successful Speakers'

Notes on Secrets of Successful Speakers
by Lilly Walters.

Effective Communication

To be able to communicate effectively with your audience you need to have a clear message.
People will typically forget 90% of what you have said after one week. What are those 10%?

Decide what the purpose of your speach is. Write it down in one sentence. It should be:

  • Precise
  • Measurable
  • Reasonable
  • Relevant to your audience
  • Obtainable within your time limit

Don’t talk about something you are not passionate about.

Know and care about your audience:

  • Are they positive or negative?
  • What do they want?
  • Are they analytical, relational, dominating, expressive?

Developing the Presentation

  • Pick a simple easily remembered theme; one central idea.
  • Only use three to four main points. Every point should be relveant, independent and advantageous.
  • Create your own material.
  • Find a suitable structure: Story, Problem-Solution, Analogy, Acronyms.
  • What, Where, When, How, Why and Who?
  • Edit without mercy. No one ever complains that a speach is too short.
  • Give it a good title.

The Presentation

  • Attention: Be silent, look at the audience, joke, surprise.
  • Convince: Ethos, make them like you. Pathos, make them laugh or cry. Logos, substantiate your position.
  • Remember: Repeat
  • Practice