Avoiding the Repository Pattern with an ORM

For many years now I’ve advocated not using the repository pattern on top of an ORM such as Entity Framework. There are many reasons why that I’ll try and cover throughout this post based on ways that I’ve seen it implemented. Meaning, this post is talking about poorly implemented approaches or pitfalls that I’ve seen.

To clarify, since this topic seems to really fire people up, I’m not saying that you shouldn’t use the repository pattern. I’m going to clarify why I don’t think under certain situations it’s very useful and other situations that I do find it useful.

This post was spurred on by a blog post and tweet:

IQueryable

The first thing I’ve seen with a repository is exposing IQueryable<T> (or DbSet<T>) from the underlying DbContext in your repository. This serves no purpose. It’s not abstracting anything at all.

What’s even worst is the consumers/callers don’t necessarily know at what point will they actually be retrieving data (doing I/O), unless you’re aware that the underlying IQueryable is coming from Entity Framework., Now when you call a method that materializes your query and actually hits the database (such as ToListAsync()).

Lazy Loading

Second, to this point is now if you have any type of navigation properties and are accessing IQueryable<T> from repository consumers, you must either eager load (via Include()) or have your consumers do the Include() or not realize all navigation properties are lazy loading.

Again, consumers are now aware of the underlying implementation that is Entity Framework.

IEnumerable

To overcome these issues, usually what comes next is avoiding the IQueryable<T> by returning an IEnumerable<T>.

The issue now is since you’re taking away control from the consumer, you must decide what data to Include() and Select() behind query methods.

What this often turns into is a pile of methods with various filtering parameters that could have been much easier expressed via a LINQ expression against the DbSet directly.

Query Objects

So if I don’t generally use the repository pattern, what do I use? Query Objects.

For querying, I’d rather have specialized objects that can return very specific data for the given use case. When implementing in vertical feature slices, as opposed to layers, each query is responsible for how it retrieves data.

The simplest solution is to use the DbContext and query directly.

The primary benefit is query objects only have dependencies that they actually require. Because each query object defines its own dependencies, you can change those dependencies without affecting other query objects.

A simple example of this is if you wanted to migrate from Entity Framework 6 to Entity Framework Core. You could migrate one query object at a time to EF Core instead of having to change over an entire repository that is highly coupled.

Testing

I can see the argument for using a repository because testing was difficult with EF6. However, with EF Core using the SQLite or the InMemory Provider, testing is incredibly easy.

I’ve written a post on how to use the SQLite provider with an in-memory database.

Testing a Query Object becomes incredibly easy without the need to mock.

Caching

Another argument for using the repository pattern is being able to swap out the implementation for a “cached repository”. I do use this pattern but in very select cases. Most times this is across bounded context were cached or stale data is acceptable.

If you decide to swap out the implementation of your repository, which was previously always hitting the database (point of truth) and now is using a cache implementation, how does that affect the callers? How quickly is the data invalidated?

Data can be stale the moment you retrieve it from the database, however adding caching to your repository without your callers knowing it can have a big impact on behavior.

Aggregate Root

One place I do often use a repository is when accessing an aggregate (in DDD Terms). My repositories often only contain two methods, Get(id) and Save(aggregateRoot).

The reason I do use a repository in this situation is that my repository usually returns an object that encapsulates my EF data model. I want it to fetch the entire object model and construct the aggregate root. The aggregate root does not expose data but only behavior (methods) to change state.

Repository Pattern Related

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Roundup #54: Service Provider Validation, Default Interface Members, BFF with ProxyKit, GC Perf

Here are the things that caught my eye recently in .NET.  I’d love to hear what you found most interesting this week.  Let me know in the comments or on Twitter.

ASP.NET Core 3: Service provider validation

In this post I describe the new “validate on build” feature that has been added to ASP.NET Core 3.0. This can be used to detect when your DI service provider is misconfigured. Specifically, the feature detects where you have a dependency on a service that you haven’t registered in the DI container.

Link: https://andrewlock.net/new-in-asp-net-core-3-service-provider-validation/

Default Interface Members, What Are They Good For?

The problem this solves is that if add a new member to an interface, every type that implements that interface will need to provide an implementation for that member. This may not be such a big deal if the interface is in your own code but as with any breaking change, in a library released to the public or other teams it can create a lot of headaches.

Link: https://daveaglick.com/posts/default-interface-members-what-are-they-good-for

Simplifying the BFF with ProxyKit – ASP.NET Core: From 0 to overkill

In this episode, we’ll revise our current backend for frontend implementation, introducing ProxyKit to simplify request routing to backing APIs, foregoing the need to implement everything manually.

Link: https://blog.codingmilitia.com/2019/09/11/aspnet-029-from-zero-to-overkill-simplifying-the-bff-with-proxykit

GC Perf Infrastructure – Part 0

In this blog entry and some future ones I will be showing off functionalities that our new GC perf infrastructure provides. Andy and I have been working on it (he did all the work; I merely played the consultant role). We will be open sourcing it soon and I wanted to give you some examples of using it and you can add these to your repertoire of perf analysis techniques when it’s available.

Link: https://devblogs.microsoft.com/dotnet/gc-perf-infrastructure-part-0/

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.

Detecting Sync over Async Code in ASP.NET Core

Sync over Async

It’s pretty easy to write some bad async code, especially when you first start using async/await. Async/await is pretty viral in .NET, which means it generally goes all the way through the stack. This can be challenging if you are trying to add async/await to an existing app and you usually end up adding sync over async code.

If you don’t use async/await correctly, and end up writing sync-over-async code, you’ll ultimately end up causing ThreadPool starvation.

Sync over Async

The term refers to making an async call but not awaiting it. Often time this is caused by calling .Wait() or .Result on the returned Task.

Ben.BlockingDetector

Ben Adams wrote a Blocking Detector for ASP.NET Core.

Start by adding the NuGet package Ben.BlockingDetector to your csproj.

At the very beginning (or the higher the better) of your Configure() method in your Startup, add the UseBlockingDetection() extension method to the IApplicationBuilder.

That’s it. Really. Now when we run our application and hit the /sync-over-async route from the example above, a warning is logged in the console (or however you have logging configured).

I’ve added the red arrow to point out our sync-over-async method in the call stack.

Microsoft.VisualStudio.Threading.Analyzers

Another great tool is the Microsoft.VisualStudio.Threading.Analyzers NuGet Package.

Static code analyzer to detect common mistakes or potential issues regarding threading and async coding.

Simply add it the PackageReference to your csproj and by default, you will immediately be getting warnings about sync over async calls. Here’s a screenshot in JetBrains Rider (which also can use Roslyn analyzers) that is notifying us of the issue.

Are you using any other analyzers or packages to detect blocking or problematic async code? Let me know in the comments or on Twitter.

Related Posts: I’ve also written a post on Lazy Async which try’s to solve the issue of deferring doing IO until it’s first needed.

Enjoy this post? Subscribe!

Subscribe to our weekly Newsletter and stay tuned.