The Repository Pattern

Here's how I implement and use the repository pattern. It's much less complicated than most of the other tutorials you'll read. If you've searched for "repository pattern tutorial" you've more than likely encountered the unit of work pattern as well. I would suggest totally ignore the unit of work pattern tutorials as I feel they will main stear you in the wrong direction creating a complicated mess. .net has the TransactionScope and this, in my opinion is a much better and cleaner way of implementing the unit of work (change tracking) pattern.

Repository... what is a repository?

A repository is an abstraction of the data source. To the code using a repository, it should not be apparent where the data is coming from.

Here's the gist:
  • Plan for one repository per table (Single Responsibility Principle)
  • It handles all crud operations
  • it should have a method to get all or get a filtered list of the data it's managing
  • it should have a method to get a specific item (usually by it's id)
  • it should have a method to save the item
  • it should have a method to remove the item

Lets look at an example. Let say we have the following table in the database
create table PERSON (
    ID int identity(1,1) primary key,
    FIIRST_NAME varchar(50),
    LAST_NAME varchar(50),
    EMAIL varchar(50)
)


Looking at this we'll create the following Model:
public class Person {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
}


Now lets assume we have an application that need to utilize the list of people store in above table. This is where we implement a repository. Again, all a repository is, is an abstraction of the data source. Here's an example repository in the typical fashion. Well start by creating an interface (recommended, but not required)
public interface IPeopleRepository {
    List<Person> GetAll();
    Person GetById(int id);
    void Save(Person person);
    void Remove(int id);
}


That's a typical interface for a repository. The scope is limited to a single entity and we described all the crud procedures required to manage a person. This is not set in stone. There are plenty of times where only get methods are needed, or maybe you can save but not remove data. Perhaps you need a more convenient method for filtering like this:
    List<Person> GetByLastName(string lastName);


In my book, that's totally ok. Depending on your need you could even split the methods between interfaces, have an interface "get" methods and one for "save/remove" methods. You might want to do this if you're using a message queue for saving.

Lets look at an implementation:
public class PersonRepository : IPersonRepository 
{
    //This is our data context, the orm or data access lib your using
    protected IDataContext Context { get; set; }

    //Normally you want to cache data access so you're not 
    //continually hitting the database
    protected ICache Cache { get; set; }

    public PersonRepository(IDataContext context, ICache cache) 
    {
        Context = context;  //Passed in via constructor injection
        Cache = cache;
    }

    public List<Person> GetAll() 
    {
        //First we'll attempt to get the item from cache
        var list = (List<Person>)Cache.Get("someCacheKey");
    
        //If there is something cached, then we're done, just pass the back
        if (list  != null) return list;

        list = new List<Person>();
        //If not then we need to get it from the database
        var results = Context.ExecSql("SELECT * FROM PERSON");
        
        foreach (var row in results) {
            //convert row to person and add to list
        }

        //Now that we have the data, let's cache so we don't have to 
        //do this again for a while
        Cache.Put("someCacheKey", list);
        return list;
    }

    public Person GetById(int id) 
    {
        //This is an easy one, use the list from GetAll() and filter it
        var list = GetAll();
        var person = list.FIrstOrDefault(p => p.Id == id);
        return person;
    }

    public void Save(Person person) 
    {
        //Save data to the database
        
        //Then clear the cache
        Cache.Clear("someCacheKey");
    }

    public void Remove(int id) 
    {
        //Remove it from the database

        //Then clear the cache
        Cache.Clear("someCacheKey");
    }
}



And there you have you're good to go. Now in the code that needs to get a list of people or a person they can get the data from the repository.

It's a pretty simple concept.





Last edited Jan 31, 2014 at 2:25 AM by amsprich, version 2