Skip to main content
Email Password   helpLost your password?

Introduction

The purpose of this article is to describe the technique I have used to implement the repository pattern in .NET applications. I will provide a brief description of the repository pattern and linq-to-sql, however, if you are unfamiliar with these technologies you should research them elsewhere. The goals of my implementation are:

Repository Pattern

The Repository Pattern, according to Martin Fowler, provides a "layer of abstraction over the mapping layer where query construction code is concentrated", to "minimize duplicate query logic". In practice it is usually a collection of data access services, grouped in a similar way to the domain model classes.

By accessing repositories via interfaces the repository pattern helps to break the dependency between the domain model and data access code. This is invaluable for unit testing because the domain model can be isolated.

I implement the repository pattern by defining one repository class for each domain model entity that requires specialized data access methods (other than the standard create, read, update and delete). If an entity does not require specialized data access methods then I will use a generic repository for that entity. A repository class contains the specialized data access methods required for its corresponding domain model entity.

The following class diagram shows an example implementation with two domain entity classes, Shape and Vertex. Shape has a specialized repository (IShapeRepository). Vertex does not have a specialized repository, so it will just use the generic repository (IRepository<Vertex>).

Repository Diagram

Linq-to-sql

Linq is a strongly typed way of querying data. Linq-to-sql is a dialect of Linq that allows the querying of a Sql Server database. It also includes object / relational mapping and tools for generating domain model classes from a database schema. Linq is an excellent addition to object / relational mappings tools because it facilitates strongly typed queries, such as:

IList<Shape> threeSidedShapes = _genericShapeRepository.FindAll(shape => shape.NumberOfSides == 3).Take(5).ToList();

IRepository<T>

The generic interface IRepository<T> defines the methods that are required on each repository.

public interface IRepository<T> where T : class
{
    /// <summary>
    /// Return all instances of type T.
    /// </summary>
    /// <returns></returns>
    IEnumerable<T> All();

    /// <summary>
    /// Return all instances of type T that match the expression exp.
    /// </summary>
    /// <param name="exp"></param>
    /// <returns></returns>
    IEnumerable<T> FindAll(Func<T, bool> exp);

    /// <summary>Returns the single entity matching the expression. Throws an exception if there is not exactly one such entity.</summary>
    /// <param name="exp"></param><returns></returns>
    T Single(Func<T, bool> exp);

    /// <summary>Returns the first element satisfying the condition.</summary>
    /// <param name="exp"></param><returns></returns>
    T First(Func<T, bool> exp);

    /// <summary>
    /// Mark an entity to be deleted when the context is saved.
    /// </summary>
    /// <param name="entity"></param>
    void MarkForDeletion(T entity);

    /// <summary>
    /// Create a new instance of type T.
    /// </summary>
    /// <returns></returns>
    T CreateInstance();

    /// <summary>Persist the data context.</summary>
    void SaveAll();
} 

Repository<T>

IRepository<T> is implemented by a generic repository base class, Repository<T>. Repository<T> is a base implementation that provides data access functionality for all entities. If an entity (T) does not require a specialized repository then its data access will be done through Repository<T>.

public class Repository<T> : IRepository<T> 
    where T : class
{
    protected IDataContextFactory _dataContextFactory;

    /// <summary>
    /// Return all instances of type T.
    /// </summary>
    /// <returns></returns>
    public IEnumerable<T> All()
    {
        return GetTable;
    }

    /// <summary>
    /// Return all instances of type T that match the expression exp.
    /// </summary>
    /// <param name="exp"></param>
    /// <returns></returns>
    public IEnumerable<T> FindAll(Func<T, bool> exp)
    {
        return GetTable.Where<T>(exp);
    }

    /// <summary>See IRepository.</summary>
    /// <param name="exp"></param><returns></returns>
    public T Single(Func<T, bool> exp)
    {
        return GetTable.Single(exp);
    }

    /// <summary>See IRepository.</summary>
    /// <param name="exp"></param><returns></returns>
    public T First(Func<T, bool> exp)
    {
        return GetTable.First(exp);
    }

    /// <summary>See IRepository.</summary>
    /// <param name="entity"></param>
    public virtual void MarkForDeletion(T entity)
    {
        _dataContextFactory.Context.GetTable<T>().DeleteOnSubmit(entity);        
    }

    /// <summary>
    /// Create a new instance of type T.
    /// </summary>
    /// <returns></returns>
    public virtual T CreateInstance()
    {
        T entity = Activator.CreateInstance<T>();
        GetTable.InsertOnSubmit(entity);
        return entity;
    }

    /// <summary>See IRepository.</summary>
    public void SaveAll()
    {
        _dataContextFactory.SaveAll();
    }

    public Repository(IDataContextFactory dataContextFactory)
    {
        _dataContextFactory = dataContextFactory;
    }
    
    #region Properties

    private string PrimaryKeyName
    {
        get { return TableMetadata.RowType.IdentityMembers[0].Name; }
    }

    private System.Data.Linq.Table<T> GetTable
    {
        get { return _dataContextFactory.Context.GetTable<T>(); }
    }

    private System.Data.Linq.Mapping.MetaTable TableMetadata
    {
        get { return _dataContextFactory.Context.Mapping.GetTable(typeof(T)); }
    }

    private System.Data.Linq.Mapping.MetaType ClassMetadata
    {
        get { return _dataContextFactory.Context.Mapping.GetMetaType(typeof(T)); }
    }

    #endregion
}

IShapeRepository & ShapeRepository

It is usually desirable to provide more specialised repositories for entity classes. If our domain included a shape entity we might like to have a ShapeRepository with a RetrieveByNumberOfSides(int sideCount) method. Such a class would be exposed to consumers as a specialized interface IShapeRepository:

    public interface IShapeRepository : IRepository<Shape>
    {
        Shape RetrieveByNumberOfSides(int sideCount)
    }
    public class ShapeRepository : Repository<Shape>, IShapeRepository
    {
        public Shape RetrieveByNumberOfSides(int sideCount)
        {
            return FindAll(shape => shape.NumberOfSides == sideCount);
        }
    }

Usage

We now have a fully functioning, decoupled repository implementation. A class might use the repositories as follows:

public class ApplicationService
{
    private IRepository<Shape> _genericShapeRepository;
    private IShapeRepository _specializedShapeRepository;

    public ApplicationService(IRepository<Shape> genericShapeRepository, IShapeRepository specializedShapeRepository)
    {
        _genericShapeRepository = genericShapeRepository;
        _specializedShapeRepository = specializedShapeRepository;
    }

    public void DoSomethingWithTheGenericRepository()
    {
        IList<Shape> threeSidedShapes = _genericShapeRepository.FindAll(shape => shape.NumberOfSides == 3).ToList();

        _genericShapeRepository.MarkForDeletion(threeSidedShapes[0]);
        _genericShapeRepository.SaveAll();
    }

    public void DoSomethingWithTheSpecializedRepository()
    {
        IList<Shape> threeSidedShapes = _specializedShapeRepository.RetrieveByNumberOfSides(3).ToList();

        _specializedShapeRepository.MarkForDeletion(threeSidedShapes[0]);
        _specializedShapeRepository.SaveAll();
    }

}
You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMy vote of 1 Pin
Don Pablone
7:56 20 Aug '09  
GeneralConfusing?! Pin
cpayne49
18:46 17 Aug '09  
GeneralRe: Confusing?! Pin
liammclennan
18:55 17 Aug '09  
Generalhow to use any function or stored proc with parameters Pin
virk0009
20:37 5 Jul '09  
Generalmissing the point... Pin
Maximus014
17:28 12 Jun '09  
AnswerSolution to Link Entity Framework Pin
Irv Lennert
9:51 1 Jun '09  
GeneralVery Good Article. Pin
habibsql
22:35 4 Apr '09  
GeneralMy vote of 1 Pin
janus007
10:39 31 Mar '09  
GeneralRe: My vote of 1 Pin
swheat
8:28 19 Apr '09  
GeneralRe: My vote of 1 Pin
liammclennan
11:51 19 Apr '09  
GeneralRe: My vote of 1 Pin
swheat
17:45 19 Apr '09  
GeneralRe: My vote of 1 Pin
liammclennan
18:09 19 Apr '09  
GeneralRe: My vote of 1 Pin
swheat
18:39 19 Apr '09  
GeneralRe: My vote of 1 Pin
liammclennan
18:48 19 Apr '09  
GeneralRe: My vote of 1 Pin
swheat
19:29 19 Apr '09  
GeneralFetchByKey Pin
Artem Smirnov
11:41 1 Dec '08  
GeneralDependency injection Pin
Artem Smirnov
12:50 29 Oct '08  
GeneralRe: Dependency injection Pin
liammclennan
13:16 29 Oct '08  
GeneralRe: Dependency injection Pin
Artem Smirnov
0:32 30 Oct '08  
GeneralHuge performance problem Pin
Rob van der Veer
7:09 18 Jul '08  
GeneralRe: Huge performance problem Pin
liammclennan
13:07 18 Jul '08  
GeneralRe: Huge performance problem Pin
Rainer Schuster
2:54 11 Sep '08  
QuestionWhat about your entity objects Pin
spyderzex
5:10 1 Jul '08  
AnswerRe: What about your entity objects Pin
liammclennan
10:46 1 Jul '08  
GeneralWhat about adding objects from one datacontext to another? Pin
azamsharp
6:35 5 Jun '08  


Last Updated 4 Jun 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009