In the software development world there exists the term “pattern”. Like its counterparts in other disciplines, a pattern is a tried and true way to do something. Developers are constantly seeking newer and more efficient ways to organize code and reduce the amount of foundation they have to do. One of the most common problems that developers use patterns to address is data access.
For developers, data access is one of the biggest bottlenecks in any data driven application. There are many popular patterns for this from Dependency Injection to Active Record, to Repository. Today I am going to explore the Repository pattern by using it to create a simple data access framework using Fluent NHibernate. Please note that this article will not discuss Fluent NHibernate (FNH) mapping’s.
To start it is necessary to understand how the Repository pattern works both in general and specifically with data access. First as you might imagine the Repository is a collection of objects that we are going to perform an action on. This can be a static action or a polymorphic action where the time is abstracted and can vary from object to object. For data access, this generally involves saving the Entities that exist in the repository. So from this sense you can think of the Repository as a specialized List. So to begin we have to define our repository, so lets think about what we will need:
- Connection to our database (abstracted)
- Way to add to the Repository
- Way to remove from the Repository
- Way to get sets of entities from the underlying connection
- Way to get single entity instances from the underlying connection
- Way to perform an operation on the entities within the repository
To make things consistent we will create a RepositoryBase class to serve as the base for our derived repository classes. There is a lot of discussion on whether to use an abstract class or interface to implement the Repository pattern. For the purposes of this demo I am going to use a base class in my approach, however, an interface would work in a similar fashion. So the basic definition:
public class RepositoryBase where T : EntityBase, new() { }
Some things to point out here. You will see that we are using a generic base class to provide a base for the inheriting classes. The generic type must be a subtype of EntityBase, this indicates that it is an entity and has certain required properties that are inherent to all entities.
The next thing I am going to add is a connection to my database, the code is updated as such:
public class RepositoryBase where T : EntityBase, new() { public ISession Session { get { return SessionProvider.GetSession(); } } }
.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; }
The ISession type is from FNH, and is basically a connection to our database and the mapping configuration for the entities, we will not be discussing how this is done for this post, for now its enough to understand that Session is what allows us to communicate with FNH and our database.
So now we need to provide the ability to Add/Remove entities from our Repository, so we add the following methods:
1: // standard generic methods
2: ///
3: /// Add an entity to the Repository to be operated on
4: ///
5: /// The entity to add
6: public void Add(T entity)
7: {
8: _itemList.Add(entity);
9: }
10:
11: ///
12: /// Remove the provided entity from the repository listing
13: ///
14: /// The entity to be removed
15: public void Remove(T entity)
16: {
17: _itemList.Remove(entity);
18: }
.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; }
This code should be pretty self explanation. The only thing to note is the _itemList private member variable of type List where T is the specified type for the RepositoryBase.
Next we need to decide how users of our Repository will READ from the database, both to get sets of entities and a single entity based on a condition. Below are my implementations:
1: ///
2: /// Return an entity based on its primary key (int)
3: ///
4: /// primary key value
5: /// Entity with the provided primary key
6: public T GetBy(int id)
7: {
8: return Session.Get(id);
9: }
10:
11: ///
12: /// Return all records for the given Type table
13: ///
14: /// entity of type T or null
15: public IEnumerable GetAll()
16: {
17: return Session.CreateCriteria(typeof(T)).List();
18: }
.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; }
One thing I would like to point is this version of the Repository makes an assumption that I have abstracted out in my final version. If you notice the GetBy generic method, it takes an int as its parameter. This is doing a lookup based on a primary key, which we are assuming is of type int. In my final version I have updated the class definition to support a second generic type, which represents the type of the primary key in the database, thus giving the class flexibility to deal with any primary key type. All the falls that these methods are masking go through the FNH session.
The final thing we need to add is the method to perform the operation. However, before we do this I would like to show you the code for EntityBase:
1: public abstract class EntityBase
2: {
3: public abstract bool IsNew { get; }
4: public abstract bool CanDelete { get; }
5: public abstract void SetDeleted(bool state);
6: public bool IsDeleted { get; protected set; }
7: }
.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; }
These are just some very simple properties, many of which are deferring their implementations until a new class is derived inheriting from EntityBase. Now here is a scaled down version of the CommitChanges method for RepositoryBase.
1: public void CommitChanges()
2: {
3: foreach (T item in _itemList)
4: {
5: if (item.IsDeleted)
6: {
7: Session.Delete(item);
8: }
9: else
10: {
11: if (item.IsNew) // is a new entity and will invoke insert
12: {
13: Session.Save(item);
14: }
15: else // is an existing entity and will invoke update
16: {
17: Session.Update(item);
18: }
19: }
20: }
21:
22: _itemList.Clear(); // clear all the entities and reset internal operations
23: }
24: }
.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; }
This is a great example of why I love polymorphism, we are ensuring that T inherits from EntityBase so that means we can use the public properties defined by EntityBase since every type that inherits from it MUST implement those members.
So there you have it, that is a basic Repository pattern base class, now lets explore how you would use this class.
In its more primitive form you could write code like such:
1: var series1 = new Series() { Name = "My Series" };
2: var series2 = new Series() { Name = "My Other Series" };
3:
4: using (var repo = new RepositoryBase())
5: {
6: repo.Add(series1);
7: repo.Add(series2);
8: repo.CommitChanges();
9: }
.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; }
In the next section, I will continue exploring the Repository pattern including some drawbacks and how developers are overcoming them.