TinRoll #4: Creating A Generic Repository

Morgan Kenyon
5 min readJul 26, 2019

--

Photo by Kelly Sikkema on Unsplash

Where we last left this project, we have filled out a lot of our API layer. We created unit tests, repositories and managers. But in doing so we noticed a lot of similar code. So I’m going to demo how to create a generic repository so we can cut down on duplicated code.

TinRoll Articles

This article is one of many documenting my simple stackoverflow clone called TinRoll. Here is a list of past articles:

Getting Started Part 1

Getting Started Part 2

TinRoll #3: Build Out the API Layer

Please check the github project for code to follow along with this article.

Duplicated Repository Code

Lets take a look at our UserRepository and QuestionRepository.

Both my repositories are basically the same. The only difference being the entity types we’re dealing with. I don’t want to rewrite this every time I introduce a new entity type.

In order to solve this problem we’ll introduce a generic repository. I found two great articles that I’m going to be using as a basis for creating my generic repository.

  1. Repository Pattern C#
  2. The generic repository is just a lazy anti-pattern

The first article talks about creating a generic repository. The second article gives a better way to utilize a repository through composition. I’m using the code from the first article and the design from the second.

C# Generics

First off, what are generics? Microsoft defines generics as:

Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. For example, by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations:

So basically, I can reuse code with multiple types while only having one implementation. Lets get started by defining a generic repository interface.

When using generics you don’t code against actual types. You code against placeholders. In our example we’re calling the placeholder T. When the app is running, the T will be replaced with a real class. In our app either a User or a Question class.

We are able to limit T to be certain types. The snippet where T : BaseEntity, we’re limiting T to be a BaseEntity type. In our project, BaseEntity is an abstract class that all of our db entities inherit from. So in effective we’re saying that T can be any of our db entities.

Here the the method signatures for our generic methods:

As you can see everything is expressed in terms of T, instead of using actual classes. So now that we have our interface, lets create our generic repository.

As described in greater detail in the second article from above. There are multiple ways of setting up a generic repository. One option would be to create a generic repository. Then all implementations inherit from that repository. This was actually the approach I started with until I read the second article.

A better option is to create a base repository and utilize it through composition. Instead of inheriting from a base repository, all of our type repositories use that base repository internally. This helps my typed repositories deal with a higher level of abstraction. The base repository has methods like Create and Get. The typed repositories have methods such as GetTopUsers, GetMostRecentlyUpdatedUsers, etc.

The other benefit of using composition is it doesn’t force a contract on on the typed repositories. If the repositories were setup through inheritance, any change to the base repository method signatures requires changes to the types repositories. As well as everything in our logic layer using the typed repositories. When using composition, if the base repository method signatures change, only the typed repositories need to change.

This is the reason why I’m creating my BaseRepository as an internal class. This class should only be used by other repositories. So I’m scoping it to prevent usage outside of the TinRoll.Data assembly (with small exceptions).

We have our methods, now I’d like to write some unit tests for these methods. The only problem being that this class is internal to the TinRoll.Data assembly. My unit tests contained in TinRoll.Test can’t access it.

Fortunately C# has a mechanism for allowing specific projects access to internal classes. The InternalsVisibleTo tag allows specific projects access to internal resources while blocking everything else.

This file allows both TinRoll.Server and TinRoll.Test access to TinRoll.Data’s internal resources. The Server project for dependency injection and the Test project for unit testing.

Unit Testing BaseRepository

Our Base Repository is going to cover the functionality that previously existed inside both UserRepository and QuestionRepository. Which that functionality has already been unit tested. So I’m going to refactor the existing Question unit tests for our BaseRepository unit tests.

These tests are using EF Core’s InMemory database. Now we’ll add a couple more unit tests to test the optional parameters.

Optional GetAsync() Parameters

The GetAsync method takes three optional parameters.

If you’re like me, I don’t often write Expression’s or Func’s. So I had to do some research to know how to utilize these parameters.

I’m not a huge fan of having to write this much boilerplate everytime I want to order by or filter. At the moment I think the tradeoff of using the BaseRepository will be worth it. We’ll see if that changes in time.

Here are the extra unit tests that use these three parameters.

This change in Repository pattern didn’t necessitate refactorings of my other repositories, managers and unit tests. My QuestionRepository definitely slimmed up a bit. Here is what my QuestionRepository looks like now.

A lot of methods are now just pass through for the BaseRepository. Like I mentioned earlier, I’m also able to describe methods is terms of the work that they’re performing. In my manager, instead of calling GetAsync and passing in an order by. I can now call the GetQuestionsByDateDescendingAsync() method. Which is easier to understanding what that method is doing.

Takeaways

I hope you enjoyed my description of how I implemented my generic repositories. Leveraging generics will allow us to repeat ourselves less as we develop more features. There’s lots more to do, and I’m looking forward to doing it!

I’m Morgan Kenyon. I’m a .NET developer working in the DFW area. I find C# a great language to use and it’s also backed by a great ecosystem, I love solving hard problems and want to continue talking about the tech I use. If you found this article helpful or thought provoking leave a comment and lets connect over LinkedIn!

Github Repo

--

--

Morgan Kenyon

I’m a software developer who works with the Microsoft stack. I love to program and write about what I’m doing!