---
title: 'Clean Up Your EF Core Queries With Specification Pattern'
source: 'https://youtube.com/watch?v=vKJnAojHllw'
video_id: 'vKJnAojHllw'
date: 2026-07-31
duration_sec: 672
---

# Clean Up Your EF Core Queries With Specification Pattern

> Source: [Clean Up Your EF Core Queries With Specification Pattern](https://youtube.com/watch?v=vKJnAojHllw)

## Summary

This video explains how the specification pattern can encapsulate EF Core queries and business logic checks in a single reusable definition. The author walks through building a base Specification<T> class, creating concrete specifications, and composing them with And/Or/Not helpers. He also compares the generated SQL, discusses trade-offs, and presents a simpler pragmatic alternative for most applications.

### Key Points

- **The core problem** [00:01] — Business rules are often duplicated between service-layer if statements and EF Core LINQ queries, causing maintenance issues.
- **Starting example** [00:42] — A promotion service chains several if statements to check customer eligibility. If any check fails, the service returns false.
- **Base Specification class** [01:11] — Create an abstract generic Specification<T> class with an abstract method returning Expression<Func<T, bool>>. This expression can run in memory or be translated to SQL.
- **Helper methods** [02:08] — Add ToExpression() to return the expression, and IsSatisfiedBy(candidate) to compile and evaluate it in memory.
- **Concrete specifications** [02:38] — Split monolithic checks into small semantically named classes (e.g., ActiveCustomerSpecification) that each implement ToExpression().
- **Parameterized specifications** [03:45] — Pass parameters like a DateTime cutoff into the constructor, removing hardcoded values and enabling runtime flexibility.
- **Composing specifications** [04:53] — And/Or/Not helpers combine specifications using AndAlso, OrElse, Expression.Not, and a visitor to unify parameters.
- **Generated SQL comparison** [07:37] — The original LINQ query hardcodes values. With the specification pattern, the SQL is parameterized, which is safer and enables query plan reuse.
- **Implicit conversion** [08:48] — Define an implicit operator from Specification<T> to Expression<Func<T, bool>> so you can skip explicit ToExpression() calls.
- **Honest verdict** [09:45] — The specification pattern is only worth its overhead when business rules must be composed dynamically at runtime. For fixed rules, a static extension method is simpler.

### Conclusion

The specification pattern is a powerful but heavy solution that shines when rules need runtime composition and query reuse. For most apps with fixed business rules, a simple static extension method on IQueryable<T> delivers the same benefits with far less code.

## Transcript

how you can implement the specification pattern to encapsulate your EF Core queries as well as your business logic checks. We're going to talk about the pros and cons of this pattern, which there are many, and I'm going to show
you a more pragmatic solution at the end. So, let's dive in. So, this is going to be our starting point, and I used a very simple example on purpose because I want to focus on the essence of the specification pattern, and this
is what I came up with. So, I've got this promotion service that encapsulates the customer is eligible for a promotion. Now, this just consists of a couple of if statements that we chain
approach where if any of the if statements gives us a negative result, pass, we return true. So, this is a simple case of some business rule check inside of a typical application. Now, what happens is you often end up
duplicating the same rule inside of your database queries. So, let's say we want database, we'd write the same rule differently inside of our link query. And one of the popular ways in
can solve this is using the specification pattern. So, let me show you how we could introduce this. I'll add a new class inside of our solution, and I'm going to call this the specification. As this is our base class
concrete specifications, I'm going to make it abstract and generic. And inside of this, I want one abstract method, which is going to return an expression of type T, and this is actually going to
represent our business rule. And here, we are going to return an expression. Now, this is a type that can encapsulate a lambda statement, and we can use it to execute a function in memory as well as pass it to EF Core. We're kind of trying
to solve both problems with the same implementation. Now, we're going to return a function that takes in an object of type T and returns a boolean implement a predicate and we're going to call this to expression. Another helper
method that I will add here is going to return a boolean right away and we're going to call this is satisfied by and this will just allow us to evaluate if the specification is satisfied for a given candidate object. So, what we want
to do is derive our predicate. And how you get to one from an expression is by calling to expression and then compile and this gives us back the raw predicate and then we can call our predicate with the candidate object and get back our
specification class. Now, what's the point of it? Well, I'm going to add a new type that's just going to be a wrapper for all of my concrete specifications and inside of here we can implement our first specification and
implement our first specification and what we want to do is encapsulate either all of these conditions together or encapsulate them one by one. So, if we encapsulate them one by one, we gain the option of composing them as needed at
runtime. So, we can check for active customers where the registered date is less than 40 days in the past or active customers who have spent less than 500 gives us a bit more flexibility. So, let's say we want to take that route and
we're going to create a type here that I will call the active customer specification. So, we implement our base specification type and we use the customer as our generic argument and we've got this method we need to
implement called to expression. So, what we can do here is just write a lambda expression that checks if the customer is active. So, now I'm going to repeat specifications and here I've got a registered before specification where we
can now provide our datetime cutoff as an argument and use that inside of the expression. So, we no longer have to hardcode the 30 days check. Instead, we can pass in the datetime object of our choice, and that decides when the cutoff
is. Our next two checks are the total spend and the eligible countries. So, I'm going to add a minimum spend specification, and then the allowed country specification. So, all of these follow the same idea where we've got our
specification object encapsulating the lambda expression that we would run for this check, and we gain the option to parameterize this as needed when we create the specification instance. Now, it's important to note that all of our
specification instances create a closure around our parameters at the time of creation. So, keep this in mind if you want to reuse the specification instance for multiple queries. Okay, so we've got our base specification type. We've got
our concrete specifications. So, how do we actually use them to simplify our promotion service as well as our EF Core Query. I mentioned that we actually want to combine our individual specifications. So, we need a way to be
able to compose them. And to save us a lot of time and headaches of writing this from scratch, I'm going to drop in the code that's going to allow us to combine our specifications. And as you might imagine, this is just another
implementation of our base specification type. But the difference here is that we can pass in two specification instances, a left and a right instance, and we want to apply the logical and operator between them. Now, expression types give
us an and also method that we can use to achieve this. Of course, we have to do use the same parameter between these. And in the same spirit, we can implement an or specification that uses or else to
implement the logical or operator. We can also implement negation of our specification by using expression not. And then here's the visitor that ties everything together. So, with these three glue pieces in place, we can add a
set of helper methods on our base specification type that's going to allow us to combine these. So, let's say I have a method called and that takes in the other specification that I want to combine it with. And what I do is create
a new and specification instance, pass in the current object and the other object I want to combine it with. I can do the same thing for the or operator. or and we're going to create a new or specification. And then for the
negation, I can say not. We're going to create a new not specification. And because this is a unary operator, we're just going to pass in the current instance. So, we wrote all of this code, but where's the payoff? Well, let's go
to our program file and we're going to write our specification here. So, let me add a comment to denote our new section. And let's create an instance of our promotion specification. We're going to start by creating our first
specification instance, which is the active customer specification. And then we want to keep chaining the conditions that we want to be part of the same specification. So, we chain our next check, which is the registered before
specification. And this has a cutoff date. So, let's add this as datetime UTC. Now, we're going to say add days minus 30 and we're going to pass this to our cutoff instance. Then let's chain our next specification, which is the
minimum spend specification. And in this case, we're going to pass in 500 as the value. And lastly, we're going to pass in a new allowed country specification. And we can pass in the codes for the allowed countries. So, before showing
you how we can use this, let me just first run the original example. I'll place a breakpoint here and I just want us to look at at the generated SQL that EF Core sends to the database. So, if I zoom in here from the Aspire dashboard,
here is the statement sent generated by EF Core that contains our is active check, checking if the date registered has the correct cutoff, if the total spend is greater than a certain value, and if the countries are in the list of
to be our baseline, and we want our specification to also give us the same value. So, going back to our example, I want to replace the promotion service with a call of our specification, and we've got this is satisfied by method is
going to implement the same logic. What about our EF Core query? Well, this lambda here is effectively an expression, and we can just take all of this, take our specification, and call to expression, and this should give us
the same behavior. Let's actually make sure that this is the case by looking at the SQL that's produced, and here's the same statement as before. However, it's not really the same, and in fact, it's improved because our new version that's
using the specification pattern is now parameterized. Notice that the check for when the customer was registered uses a parameter, as well as the total spend, this is first of all safer, and secondly, databases can reuse query
plans produced with proper parameterization, which could give us a quality of life improvement that you can make here is to define an implicit operator on our specification. So, we're going to say public static implicit
operator, we want to return an expression of function, effectively a predicate, and how we can define this is by taking in our base specification, and we just call spec to expression. So, the benefit here is you
can omit this to expression call, and just pass in the raw specification to on what we actually got here, and if all of this was worth it. So, what we had was a service to run the check on objects in memory, and a LINQ query to
run the same check with EF Core. We replaced this with a specification type, a bunch more types to be able to combine these, and then individual specification for the checks that we want to enforce. And overall, the payoff isn't all that
great if you've got very fixed business rules like what we had here. If you need to produce dynamic business rules at runtime from your application, then the specification pattern is actually worth it, but I don't recommend implementing
it yourself. Instead, there's a great library from Steve Smith or Ardalis called specification. You can get it on NuGet by installing the Ardalis to cover most of what I showed you in this video with a lot better
implementation and support for many more advanced features. And then, I promised you a pragmatic approach that also solves this. So, what you can do is create a simple class, make it static, and let this encapsulate your actual
business rules. So, what we do is make this an extension of IQueryable of our type that we want to check, and then I can say DB.Customers and call eligible for promotion, pass in the cut off date, and I effectively get the same thing
additional classes and maintain so much extra code. And if I also want to reuse this for my objects, I can make this return an expression and be able to use it with objects in memory like I showed you here. If you want to grab the source
available from the pinned comment right below. Also, let me know what you actually think about the specification pattern and if you think it's worth all the extra code that you have to write. If you enjoyed this video, consider
smashing the like button. Thanks a lot for watching, and until next time, stay for watching, and until next time, stay awesome.
