I originally posted this question on www.stackoverflow.com to get some feedback regarding the usage of `Repository` pattern in ASP.NET MVC 3 applications. I posited that the `Repository` pattern implementation was redundant when our application used LINQ with ORMs such as Nhibernate or Entity Framework Code First. Here I explain in depth why implementing `Repository` pattern is redundant in ASP.NET MVC 3 applications that uses Nhibernate 3.0 or Entity Framework Code First.
Repository Pattern as Defined
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
Popular Implementation of this Pattern
Say we are developing a blogging application and we identify the following domain models,
- public class Post
- {
- //Post specific properties here.
- }
- public class Comment
- {
- //Comment specific properties here.
- }
- //…. and other domain models.
One of the popular implementations of this pattern is as follows. First a repository via interface is defined,
- public interface IPostRepository
- {
- void Add(Post post);
- void Update(Post post);
- void Remove(Post post);
- Post GetById(Guid postId);
- ICollection GetByCategory(string category);
- }
- public interface ICommentRepository
- {
- void Add(Comment comment);
- void Update(Comment comment);
- void Remove(Comment comment);
- Comment GetById(Guid commentId);
- ICollection GetByPost(Guid postId);
- }
Then a concrete implementation. A possible Nhibernate implementation,
- public class PostRepository : IPostRepository
- {
- private ISession Session {get;set;}
- public PostRepository(ISession session)
- {
- Session = session;
- }
- public void Add(Post post)
- {
- using (var txn = Session.BeginTransaction())
- {
- session.Save(post);
- txn.Commit();
- }
- }
- public void Update(Post post)
- {
- using(var txn = session.BeginTransaction())
- {
- session.Update(post);
- txn.Commit();
- }
- }
- public void Remove(Post post)
- {
- using(var txn = Session.BeginTransaction())
- {
- session.Delete(post);
- session.Commit();
- }
- }
- public ICollection GetByCategory(string category)
- {
- return Session.Query()
- .Where(post => post.Category.Name.Equals(category)).ToList();
- }
- public Post GetById(Guid postId)
- {
- return session.Get(postId);
- }
- }
- public class CommentRepository : ICommentRepository
- {
- private ISession Session {get;set;}
- public CommentRepository(ISession session)
- {
- Session = session;
- }
- public void Add(Comment comment)
- {
- using (var txn = Session.BeginTransaction())
- {
- session.Save(comment);
- txn.Commit();
- }
- }
- public void Update(Comment comment)
- {
- using(var txn = session.BeginTransaction())
- {
- session.Update(comment);
- txn.Commit();
- }
- }
- public void Remove(Comment comment)
- {
- using(var txn = Session.BeginTransaction())
- {
- session.Delete(comment);
- session.Commit();
- }
- }
- public ICollection GetByPostId(Guid postId)
- {
- return Session.Query()
- .Where(comment => comment.PostId.Equals(postId)).ToList();
- }
- public Comment GetById(Guid commentId)
- {
- return session.Get(commentId);
- }
- }
And finally we use those repositories in our corresponding `Controller` classes – assuming we are using a **Dependency Injection Framework** to *constructor-inject* our dependencies – *Repositories* – into the *Controllers*.
- public class PostController : Controller
- {
- private IPostRepository PostRepository{get;set;}
- public PostController(IPostRepository postRepository)
- {
- PostRepository = postRepository;
- }
- [HttpPost]
- public ActionResult Create(Post post)
- {
- if(ModelState.IsValid)
- {
- PostRepository.Add(post);
- return RedirectToAction("Edit", post.Id);
- }
- return View(post);
- }
- //Rest of the CRUD controller methods
- }
- public class CommentController : Controller
- {
- private ICommentRepository CommentRepository{get;set;}
- public CommentController(ICommentRepository commentRepository)
- {
- CommentRepository = commentRepository;
- }
- [HttpPost]
- public ActionResult Create(Comment comment)
- {
- if(ModelState.IsValid)
- {
- CommentRepository.Add(comment);
- return RedirecToAction("Edit", comment.Id);
- }
- return View(comment);
- }
- // Rest of the Comment CRUD action methods.
- }
Redundancy in the Implementation of this Pattern
The `Repository` pattern implementation shown above doesn’t serve as a useful abstraction. This is mostly because the actual work of providing a *mapping layer* and a *collection like* interface is done by the ORM
- **NHibernate** and **LINQ** respectively. The `Repository` pattern here mostly acts as a thin proxy to the actual implementation. Since the abstraction – `IRepository` implementation – isn’t useful it serves more as noise rather than a useful abstraction.
Furthermore, un-necessary code is being written without any realizable benefit – either developer productivity or product quality. Because the above pattern is prone to *code bloat* it also increases the possibility of **defects** in the software system.
Because the pattern also emphasizes repeatability – in the *interface* and then in the implementation – it is also prone to the side-effects of a less DRY code.
A Possible Alternative Implementation
One possible more **DRY** and **concise** alternative would be to remove the usage of *interface* inheritance pattern and **use** the *facility* and the *API* already provided for by the ORM framework. A possible alternative implementation for an ASP.NET MVC 3 application could be as follows,
- public class PostController : Controller
- {
- private ISession Session{get;set;}
- public PostController(ISession session)
- {
- Session = session;
- }
- [HttpPost]
- public ActionResult Create(Post post)
- {
- if(ModelState.IsValid)
- {
- Session.Save(post);
- return RedirectToAction("Edit", post.Id);
- }
- return View(post);
- }
- public ActionResult Index()
- {
- return View(Session.Query());
- }
- }
Summary
The reason for emphasizing on the conciseness of the codebase is because *a concise, DRY code is less prone to defects, i.e. the more you can write the least possible lines of code the less chances you have of introducing *defects* and or *bugs* in your software system.* The implementation of the `Repository` pattern as outlined above adds to the **verbosity** and **code bloat** of the system without any realizable technical benefit.
Claudia von Furstenberg April 2, 2011 at 1:44 am
Dude, you've got me intrigued.
Could you show us your definition of the ISession interface and a sketch of its implementation, i.e. public class Session : ISession?
Bikal Gurung April 6, 2011 at 4:36 pm
@Claudia,
ISession is a NHibernate Session API interface. The concrete implementation is provided for by the Nhibernate itself. To use it we just need to declare it. In my sample I am assuming that we are using some sort of dependency injection framework to inject the concrete implementation of ISession.
Daniel Powell May 3, 2011 at 5:14 am
How do you implement getting by foreign keys and other more complex thing than just by the primary key without repeating that logic all over the place?
DavidS May 5, 2011 at 2:05 pm
Very interesting post. Simple question for you. How would you implement unit tests without hitting the database as I don't want to “pollute” the latter. Isn't that a case where the repository pattern comes in handy?
Bikal Gurung May 5, 2011 at 8:00 pm
If you want to unit test with the mocks, Mocking frameworks such as pex/mole makes it very easy to create mocks. However, from my experience I am not sure if mocking is a rigorous enough technique. For rigorous and robust testing, I tend to always use the actual database and set-up that I normally use for production. I have found that techniques such as mocking and using database that is different from production(e.g. memory only DBs) don't uncover bugs/defects that you would normally get when doing unit testing.
For example, one feature that comes to mind is the possible data mapping mismatch between mocks, memory only databases(Sql Server Compact) and MS SQL Server. The time datatype is not supported in SQL Server compact but is supported in MS SQL Server. If your eventual production environment is MS SQL Server and you are using SQL Server Compact then the test will not pass whereas in actuality it should/will pass if you were to use your production environment setting.
Furthermore, I think it might help you if you do your unit testing via the Controller action methods.
Omkar May 7, 2011 at 7:04 am
Interesting.
How about having the Add,Update,Delete and GetById methods in a generic Repository.
Having PostRepository inherit Repository and implementing GetByCategory and similar for CommentRepository.
Would that not be DRY enough and a justified use of the Repository pattern along with ORMs?
Bikal Gurung May 7, 2011 at 1:11 pm
Hi Omkar,
Modern ORMs such as Entity Framework 4.1 and NHibernate already provide for the CRUD facility out of the box. See my code sample above to see a version with repository and without the repository. In addition LINQ makes querying pretty painless and intuitive.
James July 5, 2011 at 5:26 pm
But then you're tied down to your choice of ORM.
By using the Repository pattern here, you're taking control/ownership of the Repository interface. The implementation would then be ORM specific – e.g. could start out with NHibernateRepository and maybe if you choose to later go to EntityFramework, switch to an EFRepository. The client that uses the repositories then doesn't need to be changed – still depending on IRepository, the DI container resolves it to your choice of implementation.
That way, you're flexible in choice of ORM without changing the client of the Repository layer.
By going the repository way, you're choosing to favour flexibility in this tradeoff.
James July 5, 2011 at 5:27 pm
Also, you could have defined a generic IRepository if all the repositories share similar functionality.
James July 5, 2011 at 5:27 pm
IRepository<> *
Joel Montealegre August 17, 2011 at 9:34 am
The repository pattern is more than just a thin abstraction. For instance, even the add method of a repository pattern which seems to be just a repetition is a good way of encapsulating of the ORM's native implementation. It hides the complexity of trying to remember whether the method name is “add”, “insert”, “addnew”, etc. GetByKey Method is a good example of the abstraction because you don't have to remember the actual LINQ syntax. In the future, if you decide to change ORM's, you just have to change your repository base. ORM's are fads, they change, and they upgrade. And when they do, I bet your repositories will still stay the same.
The repository also patterns enforces SoC because it addresses the problem of persistence and domain logic separately. You can both test it separately.
E September 12, 2011 at 8:42 pm
I am learning fluent nhibernate and using the repository pattern. It depends what you want to have the ability to swap out. If you want to swap out the ORM then the repository pattern seems really good.
I have two dlls: the mvc3 app and one for data access, with the ORMs and entities too. In the data access dll I have a generic IRepository interface factory like this:
public static class RepositoryFactory
{
public static IRepository<> GetRepository<>()
{
return new FNHibernateRepository<>();
}
}
The app dll never has to see which ORM the data access is using.
Maciek October 5, 2011 at 7:59 pm
Look here:
http://www.youtube.com/watch?v=PqaOxRbQmAQ&feature=related
Ayende is talking about the repository pattern
Tony October 12, 2011 at 9:30 pm
A middle ground could be to use a generic repository to abstract the ORM code away from the controllers.
Anonymous October 13, 2011 at 11:20 am
Swap out ORM? Rubbish ! Any ORM you use, you use much more functionality than just the repository. What about the configuration, custom query language for that ORM, mapping logic etc. Do you seriously think switching ORM is just switching a repository? Incredulous.
Anonymous October 13, 2011 at 11:22 am
Having a generic repository over another repository is a worse choice than using the repository in the first place. That choice provides no real benefit in terms of code quality or conciseness. In addition that abstraction serves no purpose and acts more as noise and technical debt than a useful abstraction.
E H October 23, 2011 at 2:50 am
All of the stuff that is specific to that ORM is hidden behind an interface. Bellow is what i'm using now, it's one of those common design patterns (forgot the name). The app gets a repository by newing up a Repository instance. The specific ORM is specified in the line that reads: ActualRepository = new FNHibernateRepository(); in the Repository class contructor, so that is what you would change if swapping out the ORM. All ORM functionality and config is hidden behind this interface and the app/client never sees the name of the ORM being used.
public interface IRepository<>
{
void Save(T entity);
void Update(T entity);
T GetByID(int ID);
T GetByID(String ID);
IList<> GetAll();
T GetByName(String Name);
}
public class Repository<> : IRepository<> where T:class
{
private IRepository<> ActualRepository;
public Repository()
{
ActualRepository = new FNHibernateRepository<>();
}
public void Save(T entity)
{
ActualRepository.Save(entity);
}
public void Update(T entity)
{
ActualRepository.Update(entity);
}
public T GetByID(int ID)
{
return ActualRepository.GetByID(ID);
}
public T GetByID(String ID)
{
return ActualRepository.GetByID(ID);
}
public IList<> GetAll()
{
return ActualRepository.GetAll();
}
public T GetByName(String Name)
{
return ActualRepository.GetByName(Name);
}
}
E H October 23, 2011 at 2:59 am
So swapping out would require writing a repository implementation to replace FNHibernateRepository above, including all config and everything. That might be really easy for instance if you are switching to an object database!
Jonas Gauffin December 5, 2011 at 9:26 am
You didn't address what he said. He said that there is no way to test the controller WITHOUT a database since you are using the ORM directly in them.
Jonas Gauffin December 5, 2011 at 9:27 am
How often do you switch ORM in a project? I have never done that (but have switched ORM when starting a new project)
Jonas Gauffin December 5, 2011 at 9:29 am
Using a DB is integration testing and not unit testing. You can't really predict the result when using a DB unless you reset the contents between each test (= poor performance).
Unit testing is not easy with this approach.
FeedYourHead December 13, 2011 at 3:20 pm
If you are not taking a pluggable DAL into account, why would you use dependency injection instead of just initializing stuff in the dependent code…?