In a recent project, we were requested to create a new web application that worked on an existing data model that was created using Linq2SQL and .NET Framework 3.5 SP1. We were asked to use best practices and tools of the trade in the new application, but how about the data model? Instead of directly using L2S, the best thing seemed to be creating a Repository pattern implementation on top of L2S and use that instead.

If you are new to repository pattern, here’s the definition from Martin Fowler‘s website: “A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection”. One benefit it’d brings you, is that you no longer need to run your hard-to-manage, hard-to-run suite of tests (if you have any) on a real database and you can use an in-memory collection instead.

The goal here was to create an IRepository interface that:

  • Should be used instead of L2S
  • We’d be able to mock out
  • Flexible in case of running complex queries
  • Generic implementation with CRUD operations

So we came up with this interface:

1
2
3
4
5
6
7
8
9
10
public interface IRepository<T> where T : class
{
void Insert(T item);
void Update(T item);
void Delete(T item);
void Save();
T FindByKey(long key);
T FindOne(Func<T, bool> criteria);
IEnumerable<T> FindMany(Func<T, bool> criteria);
}

Next thing would be the implementation. Now the implementation is depending on a DataContext object to do the work. We also need to get the Table<T> object from the data context to do the query over:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Repository<T> : IRepository<T> where T : class
{
private readonly DataContext _context;

public Repository(DataContext context)
{
_context = context;
}

public Table<T> Table
{
get { return _context.GetTable<T>(); }
}
}

With this, we can easily get our FindOne and FindMany methods going:

1
2
3
4
5
6
7
8
9
10
public virtual T FindOne(Func<T, bool> criteria)
{
return Table.Where(criteria)
.FirstOrDefault();
}

public virtual IEnumerable<T> FindMany(Func<T, bool> criteria)
{
return Table.Where(criteria);
}

CRUD operations are easy to do too. Just delegate the call to proper method on the data context:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public virtual void Insert(T item)
{
Table.InsertOnSubmit(item);
}

public virtual void Update(T item)
{
Table.Attach(item);
}

public virtual void Delete(T item)
{
Table.DeleteOnSubmit(item);
}

public virtual void Save()
{
_context.SubmitChanges();
}

We are on top of an old data model with no authority over it and since the existing entity classes come with no layer supertype, so how can we implement the FindById method? We have no notion of what is the PrimaryKey property of the entity. Fortunately, DataContext provides us with the necessary meta data information so we find our about all kinds things such as the primary key property name:

1
2
3
4
5
private string GetPrimaryKeyName()
{
var meta = _context.Mapping.GetTable(typeof(T));
return meta.RowType.IdentityMembers[0].Name;
}

We were not using any composite keys and all the tables had a primary key, the above code might not work in some cases.

Still we need to create a Func<T, bool> that is consisted of primary key name and value of the key to pass it to the FindOne method. With a magic touch of Expression Trees we can do so:

1
2
3
4
5
6
7
public virtual T FindByKey(long key)
{
var itemParameter = Expression.Parameter(typeof(T), "item");
var whereExpression = Expression.Lambda<Func<T, bool>>(Expression.Equal(Expression.Property(itemParameter, GetPrimaryKeyName()), Expression.Constant(key)), new[] { itemParameter });

return FindOne(whereExpression.Compile());
}

As simple as that. In next part we’ll see how we can test our solution.