Click here to Skip to main content
15,885,891 members
Articles / Programming Languages / C#

Implementing the Repository Pattern with LINQ-to-SQL

Rate me:
Please Sign up or sign in to vote.
4.00/5 (19 votes)
4 Jun 2008CPOL2 min read 247.3K   3.3K   78  
The purpose of this article is to describe the technique I have used to implement the Repository pattern in .NET applications.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Implementing the Repository Pattern with Linq-to-Sql</title>
</head>
<body>
<style type="text/css">
 .codeExample
 {
    background-color: #eeeeee;
    border: solid 1px #cccccc;	
    padding: 10px 10px 10px 10px;
    margin: 5px 5px 5px 5px;
 } 
</style>

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:<ul>
        <li>it must be a general purpose design that can be reused for many projects</li>
        <li>it must facilitate domain driven design</li>
        <li>it must facilitate unit testing and testing in general</li>
        <li>it must allow the domain model to avoid dependencies on infrastructure</li>
        <li>it must provide strongly typed querying</li>
    </ul>
    <h2>
        Repository Pattern</h2>
    <p>
        The Repository Pattern, according to Martin Fowler, provides a &quot;layer of 
        abstraction over the mapping layer where query construction code is 
        concentrated&quot;, to &quot;minimize duplicate query logic&quot;. In practice it is usually a collection of data access services, grouped in a similar way to the domain model classes.
</p>
    <p>
        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. 
</p>
    <p>    
        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. </p>
<p>
    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&lt;Vertex&gt;). 
</p>
<p>
<img src="RepositoryDiagram.gif" alt="Repository Diagram" title="Repository Diagram" />
</p>
<h2>
        Linq-to-sql</h2>
<p>
        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:</p>
        <div class="codeExample">
        IList&lt;Shape&gt; threeSidedShapes = _genericShapeRepository.FindAll(shape =&gt; shape.NumberOfSides == 3).Take(5).ToList();
        </div>
        <h3>IRepository&lt;T&gt;</h3>
    <p>
        The generic interface IRepository&lt;T&gt; defines the methods that are required on 
        each repository.
    </p>
    <a href="javascript:;" onmousedown="if(document.getElementById('IRepository').style.display == 'none'){ document.getElementById('IRepository').style.display = 'block'; }else{ document.getElementById('IRepository').style.display = 'none'; }">[[show/hide IRepository&lt;T&gt;]]</a>
    <div id="IRepository" style="display:none" class="codeExample">
<style type="text/css">
.csharpcode, .csharpcode pre
{
	font-size: small;
	color: black;
	font-family: Consolas, "Courier New", Courier, Monospace;
	
}

.csharpcode pre { margin: 0em; }

.csharpcode .rem { color: #008000; }

.csharpcode .kwrd { color: #0000ff; }

.csharpcode .str { color: #006080; }

.csharpcode .op { color: #0000c0; }

.csharpcode .preproc { color: #cc6633; }

.csharpcode .asp { background-color: #ffff00; }

.csharpcode .html { color: #800000; }

.csharpcode .attr { color: #ff0000; }

.csharpcode .alt 
{
	background-color: #f4f4f4;
	width: 100%;
	margin: 0em;
}

.csharpcode .lnum { color: #606060; }
</style><!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode" >
<span class="kwrd">public</span> <span class="kwrd">interface</span> IRepository&lt;T&gt; <span class="kwrd">where</span> T : <span class="kwrd">class</span>
{
    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Return all instances of type T.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="rem">/// &lt;returns&gt;&lt;/returns&gt;</span>
    IEnumerable&lt;T&gt; All();

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

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

    <span class="rem">/// &lt;summary&gt;Returns the first element satisfying the condition.&lt;/summary&gt;</span>
    <span class="rem">/// &lt;param name="exp"&gt;&lt;/param&gt;&lt;returns&gt;&lt;/returns&gt;</span>
    T First(Func&lt;T, <span class="kwrd">bool</span>&gt; exp);

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Mark an entity to be deleted when the context is saved.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="rem">/// &lt;param name="entity"&gt;&lt;/param&gt;</span>
    <span class="kwrd">void</span> MarkForDeletion(T entity);

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Create a new instance of type T.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="rem">/// &lt;returns&gt;&lt;/returns&gt;</span>
    T CreateInstance();

    <span class="rem">/// &lt;summary&gt;Persist the data context.&lt;/summary&gt;</span>
    <span class="kwrd">void</span> SaveAll();
} </pre></div>
<h3>Repository&lt;T&gt;</h3>
    <p>
        IRepository&lt;T&gt; is implemented by a generic repository base class, Repository&lt;T&gt;. Repository&lt;T&gt; 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&lt;T&gt;.
        </p>
        <p>
        <a href="javascript:;" onmousedown="if(document.getElementById('RepositoryT').style.display == 'none'){ document.getElementById('RepositoryT').style.display = 'block'; }else{ document.getElementById('RepositoryT').style.display = 'none'; }">[[show/hide Repository&lt;T&gt;]]</a>
        </p>
<div id="RepositoryT" style="display:none" class="codeExample">        
<!-- code formatted by http://manoli.net/csharpformat/ -->
<pre class="csharpcode">
<span class="kwrd">public</span> <span class="kwrd">class</span> Repository&lt;T&gt; : IRepository&lt;T&gt; 
    <span class="kwrd">where</span> T : <span class="kwrd">class</span>
{
    <span class="kwrd">protected</span> IDataContextFactory _dataContextFactory;

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Return all instances of type T.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="rem">/// &lt;returns&gt;&lt;/returns&gt;</span>
    <span class="kwrd">public</span> IEnumerable&lt;T&gt; All()
    {
        <span class="kwrd">return</span> GetTable;
    }

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

    <span class="rem">/// &lt;summary&gt;See IRepository.&lt;/summary&gt;</span>
    <span class="rem">/// &lt;param name="exp"&gt;&lt;/param&gt;&lt;returns&gt;&lt;/returns&gt;</span>
    <span class="kwrd">public</span> T Single(Func&lt;T, <span class="kwrd">bool</span>&gt; exp)
    {
        <span class="kwrd">return</span> GetTable.Single(exp);
    }

    <span class="rem">/// &lt;summary&gt;See IRepository.&lt;/summary&gt;</span>
    <span class="rem">/// &lt;param name="exp"&gt;&lt;/param&gt;&lt;returns&gt;&lt;/returns&gt;</span>
    <span class="kwrd">public</span> T First(Func&lt;T, <span class="kwrd">bool</span>&gt; exp)
    {
        <span class="kwrd">return</span> GetTable.First(exp);
    }

    <span class="rem">/// &lt;summary&gt;See IRepository.&lt;/summary&gt;</span>
    <span class="rem">/// &lt;param name="entity"&gt;&lt;/param&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">virtual</span> <span class="kwrd">void</span> MarkForDeletion(T entity)
    {
        _dataContextFactory.Context.GetTable&lt;T&gt;().DeleteOnSubmit(entity);        
    }

    <span class="rem">/// &lt;summary&gt;</span>
    <span class="rem">/// Create a new instance of type T.</span>
    <span class="rem">/// &lt;/summary&gt;</span>
    <span class="rem">/// &lt;returns&gt;&lt;/returns&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">virtual</span> T CreateInstance()
    {
        T entity = Activator.CreateInstance&lt;T&gt;();
        GetTable.InsertOnSubmit(entity);
        <span class="kwrd">return</span> entity;
    }

    <span class="rem">/// &lt;summary&gt;See IRepository.&lt;/summary&gt;</span>
    <span class="kwrd">public</span> <span class="kwrd">void</span> SaveAll()
    {
        _dataContextFactory.SaveAll();
    }

    <span class="kwrd">public</span> Repository(IDataContextFactory dataContextFactory)
    {
        _dataContextFactory = dataContextFactory;
    }
    
    <span class="preproc">#region</span> Properties

    <span class="kwrd">private</span> <span class="kwrd">string</span> PrimaryKeyName
    {
        get { <span class="kwrd">return</span> TableMetadata.RowType.IdentityMembers[0].Name; }
    }

    <span class="kwrd">private</span> System.Data.Linq.Table&lt;T&gt; GetTable
    {
        get { <span class="kwrd">return</span> _dataContextFactory.Context.GetTable&lt;T&gt;(); }
    }

    <span class="kwrd">private</span> System.Data.Linq.Mapping.MetaTable TableMetadata
    {
        get { <span class="kwrd">return</span> _dataContextFactory.Context.Mapping.GetTable(<span class="kwrd">typeof</span>(T)); }
    }

    <span class="kwrd">private</span> System.Data.Linq.Mapping.MetaType ClassMetadata
    {
        get { <span class="kwrd">return</span> _dataContextFactory.Context.Mapping.GetMetaType(<span class="kwrd">typeof</span>(T)); }
    }

    <span class="preproc">#endregion</span>
}</pre>
</div>
    <h3>IShapeRepository &amp; ShapeRepository</h3>
    <p>
        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:</p>
<p>
        <a href="javascript:;" onmousedown="if(document.getElementById('IShapeRepository').style.display == 'none'){ document.getElementById('IShapeRepository').style.display = 'block'; }else{ document.getElementById('IShapeRepository').style.display = 'none'; }">[[show/hide IShapeRepository]]</a>
        </p>
<div id="IShapeRepository" style="display: none;" class="codeExample">
<pre class="csharpcode">
    <span class="kwrd">public</span> <span class="kwrd">interface</span> IShapeRepository : IRepository&lt;Shape&gt;
    {
        Shape RetrieveByNumberOfSides(<span class="kwrd">int</span> sideCount)
    }</pre>
</div>
<p>
        <a href="javascript:;" onmousedown="if(document.getElementById('ShapeRepository').style.display == 'none'){ document.getElementById('ShapeRepository').style.display = 'block'; }else{ document.getElementById('ShapeRepository').style.display = 'none'; }">[[show/hide ShapeRepository]]</a>
        </p>
<div id="ShapeRepository" style="display: none;" class="codeExample">
<pre class="csharpcode">
    <span class="kwrd">public</span> <span class="kwrd">class</span> ShapeRepository : Repository&lt;Shape&gt;, IShapeRepository
    {
        <span class="kwrd">public</span> Shape RetrieveByNumberOfSides(<span class="kwrd">int</span> sideCount)
        {
            <span class="kwrd">return</span> FindAll(shape =&gt; shape.NumberOfSides == sideCount);
        }
    }</pre>
</div>
<h3>Usage</h3>
<p>
        We now have a fully functioning, decoupled repository implementation. A class might use the repositories as follows:</p>
<p>
        <a href="javascript:;" onmousedown="if(document.getElementById('ApplicationService').style.display == 'none'){ document.getElementById('ApplicationService').style.display = 'block'; }else{ document.getElementById('ApplicationService').style.display = 'none'; }">[[show/hide ApplicationService]]</a>
        </p>
        <div  id="ApplicationService" style="display: none;" class="codeExample">
<pre class="csharpcode">
<span class="kwrd">public</span> <span class="kwrd">class</span> ApplicationService
{
    <span class="kwrd">private</span> IRepository&lt;Shape&gt; _genericShapeRepository;
    <span class="kwrd">private</span> IShapeRepository _specializedShapeRepository;

    <span class="kwrd">public</span> ApplicationService(IRepository&lt;Shape&gt; genericShapeRepository, IShapeRepository specializedShapeRepository)
    {
        _genericShapeRepository = genericShapeRepository;
        _specializedShapeRepository = specializedShapeRepository;
    }

    <span class="kwrd">public</span> <span class="kwrd">void</span> DoSomethingWithTheGenericRepository()
    {
        IList&lt;Shape&gt; threeSidedShapes = _genericShapeRepository.FindAll(shape =&gt; shape.NumberOfSides == 3).ToList();

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

    <span class="kwrd">public</span> <span class="kwrd">void</span> DoSomethingWithTheSpecializedRepository()
    {
        IList&lt;Shape&gt; threeSidedShapes = _specializedShapeRepository.RetrieveByNumberOfSides(3).ToList();

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

}</pre>
        
        
        </div>
        
<p>
        &nbsp;</p>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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


Written By
Web Developer
Australia Australia
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.

Comments and Discussions