Struggling for Competence

Simple Repository Pattern

I've finally read an explanation of the repository pattern simple enough for me to understand. It's in the nerd dinner tutorial by Scott Gu, and also available in Professional ASP.NET MVC 1.0.

The repository pattern is about getting data in and out of the database. It encapsulates the persistence logic in a separate class, outside the model object. The repository pattern makes it possible to unit test the application without hitting the database.

Here's a simple example of a repository, using C# and Linq to Sql:


    public class AuthorRepository
    {
        private ZipfsLawDataContext db = new ZipfsLawDataContext(ConfigurationManager.ConnectionStrings["ZipfsLawConnectionString"].ConnectionString);

        public void Insert(Author author)
        {
            db.Authors.InsertOnSubmit(author);
        }
        public void Delete(Author author)
        {
            db.Authors.DeleteOnSubmit(author);
        }
        public void Save()
        {
            db.SubmitChanges();
        }
        public Author Get(int id)
        {
            return db.Authors.SingleOrDefault(a => a.AuthorId == id);
        }
        public IQueryable<Author> GetAll()
        {
            return db.Authors;
        }
    }

    public partial class Author
    {
        public Author(string authorName, int wordcount)
        {
            AuthorName = authorName;
            WordCount = wordcount;
        }
    }

The model object, Author, is a partial class. Linq to Sql is generating the other half of the class, providing properties for getting and setting the data values. Here are some integration tests, which actually hit the database. They show how to use the repository, and check things are working right:


    [TestClass]
    public class AuthorRepositoryTest
    {
        TransactionScope scope;

        [TestInitialize]
        public void PerTestSetUp()
        {
            scope = new TransactionScope();
        }
        [TestCleanup]
        public void PerTestTearDown()
        {
            scope.Dispose();
        }

        [TestMethod]
        public void Insert_WritesToDatabaseAndFillsInTheId()
        {
            AuthorRepository db = new AuthorRepository();
            Author author = new Author("Alice", 40000);
            db.Insert(author);
            Assert.AreEqual(0, author.AuthorId);

            db.Save();
            Assert.AreNotEqual(0, author.AuthorId);
        }

        [TestMethod]
        public void Get_ReturnsTheSameObjectByRef()
        {
            AuthorRepository db = new AuthorRepository();
            Author author = new Author("Alice", 40000);
            db.Insert(author);
            db.Save();

            Author sameAuthor = db.Get(author.AuthorId);
            Assert.AreSame(author, sameAuthor);
        }
        
        [TestMethod]
        public void Delete_RemovesTheObjectFromTheDb()
        {
            AuthorRepository db = new AuthorRepository();
            Author author = new Author("Alice", 40000);
            db.Insert(author);
            db.Save();

            db.Delete(author);
            db.Save();
            Assert.AreEqual(0, db.GetAll().Count());
        }
    }

There are some important features of the repository pattern to point out: