This topic was discussed by a lot of developers. It’s not hard to find good article about it, but only if you know what you are looking for. When I started working with EF it was much easier to find articles that gave wrong answer, that’s why for pretty long time I was using bad practice and partially tested on myself which approach is better.
Recently I was researching this topic again and decided to create article with results, so you don’t have to research it for yourself. Today, I’ll tell you a little about both approaches and present pro/cons list for both. Then you’ll be able to decide which is better for you.
Repository pattern
First of all, you should learn more about Repository Pattern. Well, it creates abstraction over persistent storage, so classes that would use your repositories should think it’s just another collection. It simplifies your application code, additionally hides data access logic, which may be pretty complex. Normally you can have there SQL code, so you’ll be able to separate SQL from C#. Last thing, your services can stay persistence ignorant, that means, they don’t need to know how data is saved in database, service will just put new item to the list, and it will appear somehow in persistent storage (database for example).
Unit of Work
Now, let’s find out what is Unit of Work. It’s pattern used to group many operations into “unit of work”, it’s usually used in database operations. Thanks to UoW, we can make sure that every operation completed successfully or they failed all. Let’s look at example, let’s say you want to create hierarchy, Category and some Tags for that category. So first of all, you need to create category, but it failed, you didn’t know about that and you’ve created tags, and then it appeared that your tags lacked category. Unit of Work would rollback whole transaction and fail whole process at the moment when inserting category failed. I used word Transaction not by accident, UoW works pretty much the same way as transactions in SQL.
DbContext
In this approach we’re not going to wrap DbContext with repository, that means we’re going to use DbContext straight in services or even controllers.
Repository + UoW pros
Reusable code + easy testing
This is the most important advantage, you can create repository for example student repository and use it everywhere to load students, it will help you creating DRY code. Additionally you’ll be able to mock repositories, so writing unit tests will be possible.
Hides IQueryable<T>
You shouldn’t return IQueryable outside. Repository may help you with that, because you can easily call ToList() at the end of method in repository, thanks to that user won’t be able to create too complex query. This is possible to achieve with DbContext as well, because in there we’re using Query Objects.
Splitted loading data logic from application logic
Thanks to that you’ll have order in your code. It’s easier to introduce code to another developer if it has some rules. Second thing is that data access logic won’t mix with application logic. So it’ll be clear which part loads data and which do the application magic.
Good for Raw SQL
Repository pattern is great for Raw SQL code, because with Raw SQL you can’t use in memory database provider that may be used to “mock” database. Additionally there is SQL code, so separating it from rest of application code is even better idea.
Can switch EF with something else without any change in service code.
Last, but not least by wrapping DbContext and using everywhere your own interface, you’ll be able to switch implementation of repository without changing anything else. It was second thing that got me for long time. I didn’t want to let EF out of its box.
Why shouldn’t use Repo + UoW?
Looking at all of these great feature provided by repository pattern I was convinced that it is way to go. But most of professional would tell you to use straight DbContext. Why?
Repo hides Entity Framework features
Because Entity Framework implements repository pattern and unit of work in DbContext. Writing Repository for DbContext you’re creating abstraction over abstraction which additionally removes some of EF features.
Example? My team and I created solution where we used Repository pattern with double mapping entity to domain objects. Thanks to that EF couldn’t update our models by itself, we had to map it on our own. We were doing couple of mappings during one update operation and it was only part of our problems, because after saving changes we had to somehow return newly created ID to controllers, because EF couldn’t map it back for us. After that project I learned on my own skin, that we shouldn’t write repositories for EF and for sure creating different entity model and domain model.
Problems with saving changes
Using repository damages UoW. We will create multiple repositories with the same DbContext, so for example we’ll have Student Repository, Course Repositry etc. The problem is that when we call SaveChanges in Student Repository DbContext will also save every other changes, so for example Course as well. User may be surprised. He may even save something that he doesn’t wish to.
Additionally there is another problem, your repositories shouldn’t save changes at the end of method, because it’s possible that in different place you’d like to use one method as one step, and you’d save those changes without knowing. So after all, Persistence Ignorance will not work here.
Multiple methods to return data in desired format
Let’s say you want to display students in list, but additionally with details or without private information for teachers. Then you’d need to create 3 methods to load data, for every situation. For example StudentListItem {name: string, age: number}, StudentDetail {name: string, address: string, birth: DateTime} etc. There isn’t good solution for this problem, the best you can do is use AutoMapper and its ProjectTo() method, which will use AutoMapper profile to create select for you based on provided class.
DbContext Pros
Everything you can do using Repo + UoW, you can also do with DbContext. If there are lines of code that repeat over and over you can enclose them inside extension method for DbContext. Thanks to that your code will be DRY and you’ll split Data Access from application logic.
Unfortunately you can’t mock Query Objects, because extension methods are static, but there’s another way.
When you write LinQ to SQL queries you can use In-Memory provider. Thanks to that you’ll be able to provide expected data. That UT will have dependency to extension method, but if you’re sure that extension method works (you have tests for it) then you can exclude extension method as source of error.
DbContext Cons
Bad for Raw SQL
There are 2 main problems with this approach. First, you shouldn’t use it when you’ll need to write Raw SQL queries. When you’re writing SQL code, you can’t use In-Memory provider so you’ll have to call database. In that case you need repository so you can mock it, as I said before, you can’t mock static methods easily, it’s possible, but with commercial tools or additional work. Writing Unit Tests is somehow harder, but still possible.
Bound to ORM
If you start using DbContext in your services then you need to make decision. From that moment you’ll tide your application with Entity Framework.
I must say I didn’t like to have big third party dependencies in my whole applications until someone told me that I’m using AutoMapper everywhere and it is third party dependency as well. Why would I make exception with AutoMapper and not EF. I said that I’d like to be able to switch ORM in future, but then I’ve realized that no one ever switched ORM in big project. When you decide to use EF you’re making that decision till last day of that project. Even if you’d prepare everything for switch, management wouldn’t let you waste time and money for something like that.
So this is design question, if you’re ready to use EF during that project, if so, you don’t have to box it. It’s better to use DbContext where it’s needed and make use of EF features.
Outro
Now you can make your decision safely. Additionally I’ve left links to blogs that I used. These are really great readings and you should check them out as well.
Next time we’ll talk about testing DbContext. We’ll find out if testing really is that though as it looks like.