7

When working with an ORM such as Entity Framework, I've fallen into a comfortable habit. Make an interface with get/add methods on it, put this on a "Repository" class then add a constructor to each class that accepts the interface. Use the interface's methods in the class.

It seems the most straightforward way to gain an abstraction layer that I can then mock out for unit tests.

However, doing a bit of reading around the subject, there seems to be a lot of confusion over what the best approach is. Some examples include:

  • Using your ORM with the repository pattern (seems a bad idea, since most ORM's are already their own repository patterns).

  • Creating a reusable repository using generics and sharing it across projects (seems like overkill unless you're working with a vast amount of different projects).

  • Having a repository that includes more detailed data logic. I.e. with methods like "GetUsersWhereAdminIsTrue" rather than the more generic "GetUsers" (this would seem to lead to a repository with a massive list of methods).

  • Using IOC to allow unit testing of code without an external source without the need for an extra wrapper layer (I've seen this mentioned several times, but I don't understand it. Surely even with IOC you still need to inject an interface?)

The answer may be partly circumstantial. But I'd like to understand why people are proposing solutions in spite of the shortcomings that I've bracketed above?

  • On the project I'm working on right now, I'm using the 3rd option. Working fine. – Michael Obi Sep 7 '15 at 10:26
  • 2
    I take 5th option : Use Repository provided by ORM. – Euphoric Sep 7 '15 at 12:06
  • My take on why EF (and other ORMs) are not repositories. – Eric King Sep 10 '15 at 22:13
  • I usually go with the 3rd option, with cross-calling so that all reads of say User eventually call a single method GetUsers with filters, so that yes, you have lots of methods, but small methods and no repeated code. Also I make a point to make GetUsers private so you can't bypass the other methods, allowing you to change the signature later without much trouble. – Neil Sep 11 '15 at 7:53
2

Maybe you could add some code to illustrate the different methods?

ORMs are great in that they give you easy access to a DB without having to type a load of boiler plate code and SQL, but they cause problems if you are a lazy and dont wrap them in a repository class.

A. you can't inject a mock for testing

B. you create tight coupling with your ORM classes

C. you cant control the way in which the ORM is used ie. drop the tables, run a low performance query etc

the points you outline are ways people have tried to get around these problems

1: hides your ORM classes from the rest of the project and allows mocking

2: I agree with you on this, but it does reduce the number of methods in 3

3: gives you control over specific queries run and limits the scope of the object

4: I assume you mean using an IoC framework as a kind of repo? sounds bad to me, but I've seen some crazy stuff done with IoC frameworks

1

I used the generic repository approach with a repository factory/provider that I got from a pluralsight class by John Papa (also used in this article). It did feel like overkill at first (and I wasn't even aware that EF used the repo pattern at that point) but was very easy to work with and ramp up the rest of the team.

Some benefits that I saw:

  • Separation of abstraction levels - I kept my db access within these repos and kept the repo usage in the service layer. There are clearly other ways to accomplish this, but this pattern felt like a guide to do so.
  • Unit tests were easy to implement with the amount of injection going on
  • The factory was helpful for only creating repos that I needed for the specific task
  • Such a large number of the model objects are just simple look-up items that using a generic repo felt so natural. More complexity can be added if you create a custom repo, also included in the article.
  • Very few changes (and well documented) to make changes to the model

The one drawback that isn't EF specific was that the dbsets for each entity are exposed publicly in the DBContext in order to seed the DB in the initializer. This means that you have to make sure that everyone is using the repos to access the data, rather than just grabbing a dbset.

1

I've been exposed to a lot of different projects in the past taking different approaches. The one I've liked the best was re-using an interface from the ORM to use throughout the code. In the case of entity framework we are normally using an DbContext with a bunch of DbSet< T>. Luckily DbSet implements IDbSet< T> which there exists in memory implementations of .

So we can define an interface and depend on the that throughout the code:

interface IFooContext
{
    IDbSet< Bar> Bars{get;}
    IDbSet< Baz> Baz{get;}
}

This is a very lightweight approach because we don't have to write the wrappers. I'm aware not all ORMs have an interface we can use like this but for those that this is an easy approach.

1

Currently, where I work, we use a repository that sits on top of EntityFramework so that we can mock it out relatively easily. It isn't quite a repository like in the Repository Pattern, however, it acts as a go between for objects using that pattern an EntityFramework.

The important part here is that it doesn't have equivalents to "GetUsers" and "GetUsersWhereAdminIsTrue", instead, we use a generic method that takes an generic object that builds its own IQueryable for that which we call Criteria. This means that this go between consists of the following generic methods, where the type is the EntityFramework object:

  • Add (takes a Users object, and adds it to the Users table)
  • Remove (takes a Users object, and remove it from the Users table)
  • Matches (takes a generic Criteria object of Users and returns a set of Users matching that criteria)
  • Save (doesn't take any parameters, but saves any pending changes)

One of the helpful parts of the Matches method is that it finalises the collection, which skips over some concerns when it comes to n+1 ORM issues.

When it comes to our actual repository objects, our equivalent to a UsersRepository may well have GetUsers and GetAdmins methods, but all these will do is create a Criteria for that requirement and pass it to our main repository. Then, the UsersRepository can sit on a mock instead of EntityFramework and still pass the same criteria through which we can test.

It's not perfect for testing EntityFramework (due to things like DbFunctions which we use Shims for), but it covers most situations without too much concern.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.