Exploring the Repository Pattern (Part 2)

In the first portion of this article we explored what the Repository pattern is and provided a basic implementation for now developers are using it to facilitate data access.  We create a basic class RepositoryBase and showed a basic example of using it.  Now we will explore this class and demonstrate how to take the Repository further and make it more useful.

Now, in what we created there is one major obvious drawback.  If we wanted to write a custom query how do we do so?  Surrounding this matter is a lot of debate on extending the pattern, for the most part I have seen the following schools of thought:

  • Query objects
  • Extension methods
  • Derived Model Repositories

Of these I find query objects to be the most fascinating, I will defer to my friend RossCode to explain them here.  But essentially the idea is to create a new abstract base class called QueryBase and use this to create objects that contain criteria for performing these custom queries.  Using this we can create two methods (one for singles, one for sets) on our RepositoryBase like such:

   1:  protected T GetBy(QueryBase query)
   2:  {
   3:       return query.SatisfyingElementFrom(GetAll());
   4:  }
   5:   
   6:  /// 
   7:  /// Get a listing of records that matches a custom criteria
   8:  /// 
   9:  /// Query object to supply the criteria data
  10:  /// ILIst of T or null
  11:  protected IEnumerable GetAll(QueryBase query)
  12:  {
  13:       return query.SatisfyingElementsFrom(GetAll());
  14:  }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.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; }

What we have done is create a set of methods that take an abstract type as its parameter, thus allowing any class that inherits from QueryBase.  In reality, this method is designed to be used with something like LINQ to NHibernate which would allow these custom queries to benefit from the deferred execution that is found in many data related variants of Linq, in our example however, we will be using standard Linq to Objects; I believe RossCode’s examples use Linq to NHibernate.

So using these query objects we can define separate classes for each custom query we wish to make and then pass then to either GetBy or GetAll.  This has the advantage of allowing us to use only one class for the repository and yields custom results.  But wait there is a slight problem with this method from a maintenance perspective.  Now we are dropping this query objects all over the application, and while this may be ok for a smaller application, in larger application it could quickly turn into a mess.  From a Separation of Concerns perspective we are not defining our queries outside the data access layer.  The caller should not need to know anything about how the query is generated, only that it can call the function and return the result.

Lets stick with the idea of Query Objects and address the root of this problem: we want to be able to make the custom calls as dumb as possible and not have query definition logic in the layer making the call.  One of the ways around this is to use extension methods (.NET 3.5).  Consider the following example:

public static List GetEpisodes(
     this RepositoryBase<Episode, int> repo,
     bool state)
{
     return repo.GetAll(new ActiveEpisodeQuery(state));
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.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; }

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.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; }

What this will do is attach a method (GetEpisodes) onto all instances of RepositoryBase thus allowing us to contain the method to a single function.  While this is not a bad approach, I do not like it because it feels so awkward; I would rather the functions be part of the class themselves, I cannot call this method through reflection should I need to later on.

My chosen way is to use derived repository classes, because:

  1. Base classes should always be declared abstract and thus not allowed to be instantiated
  2. Using a derived class allows the ability to hide more from the developer and make their paths less cluttered
  3. It builds on the OO principle of encapsulation and allows us to change things at will
  4. Using inheritance from the gives us the ability to hide things we may not need thus creating a simpler API

The major drawback to this approach is that you end up having to create and maintain more classes, because now every model will need to have a specific Repository class.  A sample one could look like such:

public class EpisodeRepository :
     RepositoryBase<Episode, int>
{
     public List GetActiveEpisodes()
     {
          return GetAll(new ActiveEpisodeQuery(true));
     }
}

.csharpcode, .csharpcode pre
{
font-size: small;
color: black;
font-family: consolas, “Courier New”, courier, monospace;
background-color: #ffffff;
/*white-space: pre;*/
}
.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; }

Using this approach you can increase the amount of code that can be encapsulated inside the derived class and thus perform more complex operations on the repository objects, all while providing a simple API to the outside.

To conclude, we talked about the major shortcoming of the basic form of the Repository pattern: customization.  In any data driven application you will need to create custom queries that mutate and translate data in different ways.  The use of query objects can greatly enhance the clarity of the underlying logic, but without an additional layer of abstraction could pose great problems for code maintenance. To mitigate this we analyzed two possible approaches: 1) use extension methods to extend possible instantiations 2) create derived repositories for models to provide these custom data manipulations methods.

It is my personal preference to use approach #2 as it enables you to achieve complex operations with a much simpler interface.  It also helps with code maintenance and readability as the code can describe to the developer what it is doing and what is involved in the operation.

Advertisement

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s