Click here to Skip to main content
Click here to Skip to main content

Implementing the Repository Pattern with LINQ-to-SQL

By , 4 Jun 2008
 

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:

  • it must be a general purpose design that can be reused for many projects
  • it must facilitate domain driven design
  • it must facilitate unit testing and testing in general
  • it must allow the domain model to avoid dependencies on infrastructure
  • it must provide strongly typed querying

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 the 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 mapping 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 and 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();
    }

}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

liammclennan
Web Developer
Australia Australia
Member
Liam McLennan has been developing for the internet since 2001. He is passionate about delivering top quality I.T. solutions that address his customer's needs.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 2memberGastonV7 Feb '13 - 22:49 
I don't like the way that the repository is being used for 1 object / table. In most of my repositories I put all the relevant objects together. Ex: master-detail tables would be in 1 repository, possibly with some satelite tables around it.
GeneralVB.NET versionmemberbayonian28 Nov '09 - 2:29 
Can you show us this sample in VB.NET please?
 
Thank you.
GeneralMy vote of 1memberDon Pablone20 Aug '09 - 6:56 
Poor info
GeneralConfusing?!membercpayne4917 Aug '09 - 17:46 
Hi,
 
I agree with the others, its well written but confusing.
 
I think you've gone too far in trying to keep it simple.
 
Should I create a new repository for each object?
 
If you were to use Student tracking as an example. Am I creating an IRepository for each Student, Class, Result etc??
GeneralRe: Confusing?!memberliammclennan17 Aug '09 - 17:55 
That question is too complicated to address in this article. You will need to read the book.
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

Questionhow to use any function or stored proc with parametersmembervirk00095 Jul '09 - 19:37 
Hi,
I have used your codes in my project. But I am having one prblem.
I have 2 stored procs in my database,
I want to call them with some parameters, how Will I do that
with my generic repository.
 
Regards
Paraminder
Generalmissing the point...memberMaximus01412 Jun '09 - 16:28 
I agree with quite a few of the others that have already written comments; the code is well written. Unfortunately, it misses the point of the repository pattern. By using the classes created by LINQ to SQL, you have tied yourself to an implementation (and incidentally your database structure). You need to create model objects distinct from your LINQ to SQL classes and do your field mapping where needed. Also, the MarkForDeletion and SaveAll methods limit your underlying data access to something that keeps track of your changes.
AnswerSolution to Link Entity FrameworkmemberIrv Lennert1 Jun '09 - 8:51 
Liam,
 
Excellent article! I like your interpretation of Repository pattern very much, so much I used it. However, I needed to use Link to Entities because I'm using inheritance in my model, also I thought I would change two things to make your solution more flexible and performant. It's all simple enough so here it is:
 
The IRepository interface is almost identical:
 
using System.Linq;
using System.Linq.Expressions;
 
namespace System.Linq.Repository
{
public interface IRepository<T> where T : class
{
/// <summary>
/// Return all instances of type T.
/// </summary>
/// <returns></returns>
IQueryable<T> All();
 
/// <summary>
/// Return all instances of type T that match the expression exp.
/// </summary>
/// <param name="exp"></param>
/// <returns></returns>
IQueryable<T> FindAll(System.Linq.Expressions.Expression<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(System.Linq.Expressions.Expression<Func<T, bool>> exp);
 
/// <summary>Returns the first element satisfying the condition.</summary>
/// <param name="exp"></param><returns></returns>
T First(System.Linq.Expressions.Expression<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();
}
}
 
And the implementation of Repository for Entities here:
 
using System.Linq;
 
namespace System.Linq.Repository
{
public class Repository<T> : IRepository<T> where T : class
{
protected System.Data.Objects.ObjectContext _dataContext;
 
public Repository(System.Data.Objects.ObjectContext context)
{
_dataContext = context;
}
 
#region IRepository<T> Members
 
public IQueryable<T> All()
{
return _dataContext.CreateQuery<T>(typeof(T).Name);
}
 
public IQueryable<T> FindAll(System.Linq.Expressions.Expression<Func<T, bool>> exp)
{
return All().Where<T>(exp);
}
 
public T Single(System.Linq.Expressions.Expression<Func<T, bool>> exp)
{
return All().Single<T>(exp);
}
 
public T First(System.Linq.Expressions.Expression<Func<T, bool>> exp)
{
return All().First<T>(exp);
}
 
public void MarkForDeletion(T entity)
{
_dataContext.DeleteObject(entity);
}
 
public T CreateInstance()
{
T entity = Activator.CreateInstance<T>();
_dataContext.AddObject(typeof(T).Name, entity);
return entity;
}
 
public void SaveAll()
{
_dataContext.SaveChanges();
}
 
#endregion
}
}
 

Please, let me know wat you think.
 
Thank you,
 
Irv
GeneralVery Good Article.memberhabibsql4 Apr '09 - 21:35 
Thanks for very good article. I request to you, in future you will write similar article for Entity Framework. I think that will very helpful for like me who are interested to use Entity Framework as OR Mapping tool.
GeneralMy vote of 1memberjanus00731 Mar '09 - 9:39 
It has nothing to do with a real setup.
GeneralRe: My vote of 1memberswheat19 Apr '09 - 7:28 
Absolutely correct. This is great in theory. In practice it's useless.
GeneralRe: My vote of 1memberliammclennan19 Apr '09 - 10:51 
I think it is useful. Care to elaborate on your comment?
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: My vote of 1memberswheat19 Apr '09 - 16:45 
Firstly, thank you for not blasting me for sounding rude. Your code seems to be well thought out and well written. I have not actually stepped through it. My problem is not with your code, but with Linq to SQL, a technology I greatly enjoy but whose shortcomings give me great angst. L2S has great potential. Unfortunately, in its current implementation, as it relates to web applications, L2S is little more than a glorified pipe to a database. Dont get me wrong - There are a lot of things I like about L2S - namely entity class generation. But as far as functioning as a repository, L2S does not (for n tiered apps). The code you've written is good but in IMHO is written against a model that is not yet complete.
 
Here is a question I posed to Microsoft. You can read it here[^]
 
My basic question was: My objective is to allow the user to create an entire order without writing anything to the database. Once the user clicks save I want to write the whole thing at once. How do I accomplish that?
 
The relevant portion of the answer is:"...if you use entity objects to store data collected from users before submitting to the database you need to track entity state (new/modified/deleted etc) yourself across postbacks/page lifecycles and then use the L2S datacontext to read/write from the database."
 
So all the state tracking built into L2S is not only useless but cumbersome. L2S is not a repository. It does not afford me any mechanism for tracking entity state across layers of my application. It provides minimal support for (de)serialization, and I have to manage storage of objects in viewstate or session myself. That should be L2S's job! The code you wrote, while filling a certain need, does not appear to address this deficiency. Thus it becomes more icing on an inedible cake.
 
I'd love to be wrong about this because I'm knee deep in a very large app where these problems are killing my schedule!
 
Thanks for listening Smile | :)
GeneralRe: My vote of 1memberliammclennan19 Apr '09 - 17:09 
Good news! I think you are wrong Smile | :)
 
I agree that Linq-to-sql has some terrible flaws. And it seems now that they will never be fixed, however, your problem is with the web and the stateless nature of the HTTP protocol. Nearly everything in a web app happens in the lifetime of a HTTP request so there is no good way to persist state across requests. The problem you described exists in linq-to-sql, nhibernate, entity framework, ruby on rails and every other web development environment.
 
NHibernate has a solution called session-per-conversation which effectively uses the asp.net session to store the object context between requests. The other options are to use ajax to rewrite your application so that your entire order is created in a single HTTP request. Finally you can have a way to flag the various parts of the order as not being complete until the final save.
 
Don't store objects in session / viewstate. There are great performance problems with such an approach. Either keep everything on the same page, or persist to the database.
 
I like your cake metaphor.
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: My vote of 1memberswheat19 Apr '09 - 17:39 
>>.....so there is no good way to persist state across requests.
 
So then how am I wrong?
 
>>The other options are to use ajax to rewrite your application so that your entire order is created in a single HTTP request. Finally you can have a way to flag the various parts of the order as not being complete until the final save.
 
Yikes!! Do you have any idea how difficult that would be!! Plus there are concurrency issues.
 
I really need a repository that is in fact, a repository. And, as you said in your article, it should be reusable, domain driven, independent, and strongly typed. I should not have to write to temp tables or wrestle with session or write out temp files. Indeed, L2S would be just fine for me if it did what it's documentation says it does.
 
Just a few hours ago I stumbled on .net RIA which you can read about here.[^]
I'm not sure if is just another blind alley but it looks interesting.
GeneralRe: My vote of 1memberliammclennan19 Apr '09 - 17:48 
You are wrong because you are blaming Linq-to-sql for the nature of web applications.
 
What you need is to be a desktop / smart client / silverlight application.
 
What does the linq-to-sql documentation say that is not true?
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: My vote of 1memberswheat19 Apr '09 - 18:29 
I know I sound bitter but actually I'm not casting blame Smile | :) If I were to blame L2S, it would not be for the nature of web apps, but for a failure to work with the nature of web apps. I'm sure there are ways for L2S to work with a stateless protocol. You can probably think of one or two yourself right now. For example, wouldn't it be great to be able to call methods to serialize and write a datacontext to a row on a temp table. And another method to read and deserialize it. If something like that could be used with code like you've written in your article, the appearance to the other layers in the app would be one of a stateful repository.
For the record I am not proposing the preceeding as a single solution to the problems I've discussed. It is just an example I've pulled out of thin air to make the point that some kind of stateful repository is not an impossible objective. It may not be optimal or scalable, but it's not impossible. See this[^]
GeneralFetchByKeymemberArtem Smirnov1 Dec '08 - 10:41 
Is there any possibility to implement something like T FetchByKey(object key)? I tried using FindAll with a Func that checked the key property value, but of course this could not be translated to SQL so I failed.
 
Any ideas?
GeneralDependency injectionmemberArtem Smirnov29 Oct '08 - 11:50 
I'm a bit confused: when you create a repository, you inject an instance of IDataContextFactory. Why not an instance of DataContext itself? If the factory is important, could you provide an implementation?
GeneralRe: Dependency injectionmemberliammclennan29 Oct '08 - 12:16 
IDataContextFactory exists to provide a place to manage the scope of the datacontext. Im my case I wanted the scope of the datacontext to be a single http request.
 
There is an implementation of IDataContextFactory in the code download attached to this article.
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: Dependency injectionmemberArtem Smirnov29 Oct '08 - 23:32 
Hmm could you please check it again? I couldn't find any implementation. Do you just keep the context in HttpContext? Why put SaveAll in the factory interface, isn't it the context's concern?
 
I'm totally new to the DLinq stuff, and I'm trying to build my first DLinq Web site based on your article. Frankly, I don't have a clue about the scope, so any further info would be appreciated.
 
So, I think your article is good, but it could be improved in either removing the Factory stuff (since it's not absolutely required for DLinq and leaves questions for newbies like me), or explain a little about why do you need the Factory.
 
ulu
 
http://sm-art.biz[^]
GeneralHuge performance problemmemberRob van der Veer18 Jul '08 - 6:09 
I tried your approach because it sounded very nice. I do have some comments I'd like to share.
 
By defining your virtual methods like this: public IEnumerable<T> FindAll(Func<T, bool> exp) you create a enormous bottleneck. This is because the Func will not be (what I call) a delayed method, but it will be expanded immediately. I checked the log from the datacontext and it shows that the table is no longer queries using a where clause. Instead, a full table load is performed and the function is evaluated in memory.
 
However, the solution is very easy, although this mean you'll have to include the Linq assemblies to your testing bed:
 
protected T Single(System.Linq.Expressions.Expression<Func<T, bool>> exp)
{
   return Items.Single(exp);
}
 
The trick is to wrap you functions in a System.Linq expression. Client usage is exactly the same. The performance gain on my project was huge, If you consider that tables are no longer loaded completely into memory.
GeneralRe: Huge performance problemmemberliammclennan18 Jul '08 - 12:07 
Thanks Rob, I will give that a go.
 
Liam McLennan
liam@eclipsewebsolutions.com.au
www.eclipsewebsolutions.com.au

GeneralRe: Huge performance problemmemberRainer Schuster11 Sep '08 - 1:54 
Have a look at Ayende 'Oren Eini' Rahiens Rhino.Commons, where an IRepository is implemented. Especially for NHibernate.
 
____________________________________________
 
Supported German .NET Resources:
http://dotnet-forum.de | http://dotnet-snippets.de
____________________________________________

QuestionWhat about your entity objectsmemberspyderzex1 Jul '08 - 4:10 
I was curious how you are using the entity objects. Are you passing the "Shape" and "Vertex" Linq-to-SQL entities through the rest of your tiers? or are you creating separate business data transfer objects and mapping the data from the linq entity to them?
 
I ask because I have implemented a similar method and was curious how you got past this difficult problem.
 
Thank you,
 
Dan

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 5 Jun 2008
Article Copyright 2008 by liammclennan
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid