Specification Pattern in ASP.NET Core – Enhancing Generic Repository Pattern

In this article, we will talk about implementing Specification Pattern in ASP.NET Core applications and how it can enhance the already existing Generic Repository Patterns. We will be building from scratch, an ASP.NET Core WebAPI with Generic Repository Pattern, Entity Framework Core and finally implement the Specification Design Pattern. You can find the complete source code of this implementation here. Let’s get started.

Understanding the Specification Pattern: The Why?

Let’s walk through a simple example to understand the need to use Specification Pattern. Below is a class snippet of Developer with the required properties like Name, Email, Experience, and so on.

public class Developer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int YearsOfExperience {get;set;}
    public decimal EstimatedIncome {get;set;}
    public int Followers { get; set; }
}

Now, we would be probably having a service layer that returns dataset from the DB over an abstraction like Entity Framework Core. Here is how it would look like.

public class DeveloperService : IDeveloperService
{
    private readonly ApplicationDbContext _context;
    public DeveloperService(ApplicationDbContext context)
    {
        _context = context;
    }
    public async Task<IEnumerable<Developer>> GetDeveloperCount()
    {
        // return a count of all developers in the database
    }
}

Although you would be getting the count of all developers, a more practical and logical requirement would be to get the count of developers with some kind of filters, agree? For instance, get the count of developers whose estimated income is $100,000 or more, or the developers with 5 years of experience or more. The possibilities are quite limitless.

However, this would end in you having tons of service layer functions like GetDeveloperCountWithSalariesGreaterThan(decimal minSalary), GetDeveloperCountWithExperienceMoreThan(int minExp), and much more. The more requirement comes in, the more is the count of functions you would end up having. And what if you need the count of developers with salaries greater than x and experience higher than y years? That’s yet another challenge that may result in extra methods.

You may argue that you can directly apply these filters to the Entity Framework Core Entity, something like

await _context.Developers.Where(a=>a.Salary > 10000 && a.Experience > 6).ToListAsync()

But, NO. This is nowhere near a clean application codebase you would need. This approach will end up messing up the scalability of your application quite soon and trust me, this is not at all maintainable. Quick Tip, You always need a service layer within your application that sits between your application and Database and is solely responsible to handle the Business Logics.

This is where your application needs to use Specification pattern. Heads up, there are a few limitations to the Generic Repository Pattern which are resolved with the usage of Specification Pattern. We will build up a project and come to a point where you would practically need to use Specification.

What we will Build.

For demonstration of Specification Pattern in ASP.NET Core, we will build a simple WebAPI Application that has 2 endpoints:

  • Returns a Specific Developer Detail
  • Returns a List of Developers

However, we will add a combination of Generic Repository Pattern along with Unit Of Work to the mix to make this implementation a more logical and practical one. We will specifically identify and implement the use case of Specification Pattern here. This will be almost everything that you would need while building complete applications with ASP.NET Core 5.0. Let’s get started.

PS, you can find the entire source code of this implementation here.

Setting up the Project

First up, let’s open up Visual Studio 2019+ and create a new Solution and a WebAPI Project. Note that we will be following a Hexagonal Architecture in this implementation as well, to keep the solution well organized.

To learn more about Hexagonal / Onion Architecture in ASP.NET Core, please refer to this article – Onion Architecture In ASP.NET Core With CQRS – Detailed

devenv EquCGX4Ngh Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern
image 18 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

Once the API Project is added, let’s add 2 more Class Library projects to this solution. Let’s call it Data & Core.

  • Data is where all the implementations related to the database and contexts live.
  • Core is where we will be adding in the interfaces and domain entities.

This is how the solution would look like at this stage.

image 19 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

Adding the Required Model

As mentioned earlier, in your Core Project, create a new folder named Entities and add 2 classes to it, namely Developer and Address.

public class Address
{
    public int Id { get; set; }
    public string City { get; set; }
    public string Street { get; set; }
}
public class Developer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public int YearsOfExperience { get; set; }
    public decimal EstimatedIncome { get; set; }
    public Address Address { get; set; }
}

Adding the DBContext ,Migrations & Required Packages

Now, let’s install the required NuGet packages to the respective projects.

Open up the Package Manager Console and set the Data Project as the default project from the drop-down. Run the following commands to install the required packages.

Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
Install-Package Microsoft.EntityFrameworkCore.Tools

Next, set the API project as the default project, and run the following command.

Install-Package Microsoft.EntityFrameworkCore.Design

Before setting up the Application Context class, let’s add in the Connection String. For this, open up the appsettings.json from the API Project and add in the following.

Note that we are currently using SQLServer Local DB for this demonstration.

"ConnectionStrings": {
  "DefaultConnection": "Data Source=(localdb)\\mssqllocaldb;Initial Catalog=specification-pattern-demo;Integrated Security=True;MultipleActiveResultSets=True"
},

With that done, let’s create the required context class that will help us to access the database. For this, under the Data Project, add in a new class and name it ApplicationDbContext.

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions options) : base(options)
    {
    }
    public DbSet<Developer> Developers { get; set; }
    public DbSet<Address> Addresses { get; set; }
}

Here, you can see that we are mentioning both the Developer and Address classes to be in included in the Application Db Context.

Next, we need to add this context to our ASP.NET Core Application’s service container and configure the connection details as well. Open up the Startup.cs in the API Project and add in the following under the ConfigureServices method.

services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

Finally, we are ready to add the migrations and update the database as well. Open up the package manager console once again and set the Data project as the default project. Run the following commands:

add-migration initial
update-database

Here is a screenshot demonstrating the same. Note that you may receive warnings regarding the Precision of the mentioned decimal properties. We can ignore it for now.

image 20 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

With that done, our database should be ready now with the required tables and the corresponding fields. For demo purposes, I am adding in some sample data directly to the database using the SQL Server Object Explorer tool of Visual Studio 2019 IDE.

image 21 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern
image 22 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

Implementing Generic Repository Pattern

As our requirement is to return result sets of Developers, let’s create a Generic Repository Pattern so that it can use up the ApplicationDbContext to query data from the database. The importance of using a generic repository pattern is that this code can be reused for multiple other entities as well.

To get a detailed understanding of Repository Pattern, I highly recommend that you go through this article – Repository Pattern in ASP.NET Core – Ultimate Guide

For instance, we add a new entity named Product later, you do not necessarily need to add in a new class for Accessing Product data from the database, but you can use the already existing generic repository implementation for most of the use cases. Note that there are a few limitations for generic repository patterns which we will discuss and resolve in the later section of the article.

Under the Core section, Add a new folder and name it Interfaces. Here, add in a new interface, IGenericRepository.

public interface IGenericRepository<T> where T: class
{
    Task<T> GetByIdAsync(int id);
    Task<List<T>> GetAllAsync();
}

Note that we are only interested in the Query Operations in this Repository Implementation. Hence, we will only look in to the GetById and GetAll Methods here.

Creating Generic Repository Implementation

Now, let’s implement the above created interfaces. Since we are following a Hexagonal / Onion Architecture, we will have to add the implementations outside the Core of the Application. This means, all the data related implementations are to be added in the Data Project.

Here, add a new class, GenericRepository.

Make sure to add the required project references. Data project refers to Core Project. Core Project has 0 project dependencies. API Project depends on Data as well.

public class GenericRepository<T> : IGenericRepository<T> where T : class
{
    protected readonly ApplicationDbContext _context;
    public GenericRepository(ApplicationDbContext context)
    {
        _context = context;
    }
    public async Task<List<T>> GetAllAsync()
    {
        return await _context.Set<T>().ToListAsync();
    }
    public async Task<T> GetByIdAsync(int id)
    {
        return await _context.Set<T>().FindAsync();
    }
}

You can see that we are injecting the instance of the ApplicationDbContext to the constructor of this repository Implementation. This instance is further used to read data from the Database.

Finally, in the Startup.cs of the API Project, add in the following to register the IGenericRepository interfaces to the service container of the application.

services.AddScoped(typeof(IGenericRepository<>), (typeof(GenericRepository<>)));

Problem with Generic Repository Pattern: Anti-Pattern?

Generic Repository is considered by some developers as an anti-pattern. If used incorrectly, yes, any pattern will mess up your code. The major complaint about Generic Repository is that a single method could potentially expose the entire database access code to the user. It could also mean the need for multiple methods for each and every combination of requirements (as mentioned in the beginning of this article). For instance, look at the following interface declaration –

List<T> FindAsync(Expression<Func<T, bool>> query);

This method can be a part of the Generic Repository Pattern to combat the issue we have. But since the method is too generalized, it’s not possible for the Generic Repository to know about the expressions we pass to it. Another idea could be to remove this method from IGenericRepository interface and use it in a new interface, let’s say, IDeveloperRepository which derives from IGenericRepository. This could work, but considering the future additions of entities and requirement changes, this change would not be a smart one to proceed with.

Imagine having 20-30 new entities and having to create tons of new repositories? Not a good idea, yeah? Think about having multiple methods in IDevloperRepository and its implementation like GetDevelopersWithSalariesGreaterThan(decimal salary) & GetDevelopersWithExperienceLessThan(int years), not clean, yeah?

What if there is a much cleaner way to tackle this requirement? This is exactly where Specification Pattern comes handy.

Enhancing Repository Pattern with Specification Pattern in ASP.NET Core

Specification Pattern may feel complex at the first glance. I felt it too. But once you have added certain Base Classes and Evaluators, all you have to do is to create Specification Classes which are usually 2 – 10 lines depending on your requirement. Let’s get started with Specification Pattern In ASP.NET Core.

Under the Core Project, add a new folder and name it Specifications. This is where all the specification related interfaces would go to.

Create a new Interface and name it, ISpecification.cs

public interface ISpecification<T>
{
    Expression<Func<T, bool>> Criteria { get; }
    List<Expression<Func<T, object>>> Includes { get; }
    Expression<Func<T, object>> OrderBy { get; }
    Expression<Func<T, object>> OrderByDescending { get; }
}

This is a minimal implementation only. Let me explain each of the declared method definitions.

  • Criteria – This is where you can add in the expressions based on your entity.
  • Includes – If you want to include foreign keyed table data, you could add it using this method.
  • OrderBy and OrderByDescending are quite self-explanatory.

Next, in the same folder, add in a new class, BaseSpecifcation. This will be the implementation of the ISpecification Interface.

public class BaseSpecifcation<T> : ISpecification<T>
{
    public BaseSpecifcation()
    {
    }
    public BaseSpecifcation(Expression<Func<T, bool>> criteria)
    {
        Criteria = criteria;
    }
    public Expression<Func<T, bool>> Criteria { get; }
    public List<Expression<Func<T, object>>> Includes { get; } = new List<Expression<Func<T, object>>>();
    public Expression<Func<T, object>> OrderBy { get; private set; }
    public Expression<Func<T, object>> OrderByDescending { get; private set; }
    protected void AddInclude(Expression<Func<T, object>> includeExpression)
    {
        Includes.Add(includeExpression);
    }
    protected void AddOrderBy(Expression<Func<T, object>> orderByExpression)
    {
        OrderBy = orderByExpression;
    }
    protected void AddOrderByDescending(Expression<Func<T, object>> orderByDescExpression)
    {
        OrderByDescending = orderByDescExpression;
    }
}

Here, we will be adding 3 essential methods and a constructor.

  • Adds the expressions to the Includes property
  • Adds the expressions to OrderBy Property
  • Adds the expressions to OrderByDescending Property
  • You can note that we also have a constructor that takes in criteria. Criteria can be like ( x=>x.Salary > 100 ) and so on. You get the point, yeah?

Upgrading the Generic Repository.

To get started, let’s add a method in our IGenericRepository interface.

IEnumerable<T> FindWithSpecificationPattern(ISpecification<T> specification = null);

Next, let’s implement the new method in the GenericRepository class.

public IEnumerable<T> FindWithSpecificationPattern(ISpecification<T> specification = null)
{
    return SpecificationEvaluator<T>.GetQuery(_context.Set<T>().AsQueryable(), specification);
}

Now, the idea behind setting all this up is to create separate specification classes that can return specific result sets. Each of these new specification classes will inherit from the BaseSpecification class. Get the idea? Let’s create those Specification classes now so that it makes sense 😉

So, let’s draw out 2 requirements / specifications:

  1. A specification to return list of developers in the decreasing order of salary.
  2. Another specification that returns a list of developers with an experience of N or above along with their addresses.

Under the same Specification folder of the Core project, let’s add our first specification class, DeveloperByIncomeSpecification

public class DeveloperByIncomeSpecification : BaseSpecifcation<Developer>
{
    public DeveloperByIncomeSpecification()
    {            
        AddOrderByDescending(x => x.EstimatedIncome);
    }
}

Here, you can see that we are deriving from the BaseSpecification class and using the AddOrderByDescending method within the constructor. This specification would ideally return a list of developers with the decreasing order of their income.

Next, let’s add another class, DeveloperWithAddressSpecification

public class DeveloperWithAddressSpecification : BaseSpecifcation<Developer>
{
    public DeveloperWithAddressSpecification(int years) : base(x=>x.EstimatedIncome > years)
    {
        AddInclude(x => x.Address);
    }
}

So, here we are passing the query expression to the base of the Specification Class which is the BaseSpecification’s constructor, which in turn will add it to the Criteria property that we had created earlier. Quite simple, actually.

Now, with our specification classes ready, let’s add the api endpoints.

Under the API project, add a new API Controller under the Controllers folder and name it DevelopersController.

public class DevelopersController : ControllerBase
{
    public readonly IGenericRepository<Developer> _repository;
    public DevelopersController(IGenericRepository<Developer> repository)
    {
        _repository = repository;
    }
    [HttpGet]
    public async Task<IActionResult> GetAll()
    {
        var developers = await _repository.GetAllAsync();
        return Ok(developers);
    }
    [HttpGet("{id}")]
    public async Task<IActionResult> GetById(int id)
    {
        var developer = await _repository.GetByIdAsync(id);
        return Ok(developer);
    }
    [HttpGet("specify")]
    public async Task<IActionResult> Specify()
    {
        var specification = new DeveloperWithAddressSpecification(3);
        //var specification = new DeveloperByIncomeSpecification();
        var developers = _repository.FindWithSpecificationPattern(specification);
        return Ok(developers);
    }
}

Lines 3 – 7: Injecting IGenericRepository to the constructor of the Controller.
Lines 8 – 19: Standard endpoints that make use of the repository instance to return all the developers and the one with specific Id.

Lines 20 – 27: This is the most interesting part of the controller. Here Line 23 and 24 as you can see are the 2 specification classes we created earlier. This is just to demonstrate that any such Specification instance can be created at either the Controller or wherever the GenericRepository is being consumed. We’ll use the DeveloperWithAddressSpecification(3) for demonstration.

Let’s run the application now and check the result from the specify endpoint.

image 23 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

You can see that the Address data is also returned. Now, go back to the controller, comment out line 24 and let’s use the DeveloperByIncomeSpecification for now. Again, run the application.

image 24 Specification Pattern in ASP.NET Core - Enhancing Generic Repository Pattern

Now you can notice that there no Address data being returned. Why? Simple, because we used a different specification that does not mention the addition of the Address entity. Rather this specification returns the set of developers in the decreasing order of their income. Simple, yet neat right? This is probably one of the coolest design patterns to have on your ASP.NET Core applications.

Weird, but this is actually when you can understand what Specification pattern is 😛 According to Wikipedia – In computer programming, the specification pattern is a particular software design pattern, whereby business rules can be recombined by chaining the business rules together using boolean logic. The pattern is frequently used in the context of domain-driven design.

Makes much more sense now, yeah? Business Rules (our requirement to return developers with a certain level of experience or higher) are combined by chaining criteria (this happened in the DeveloperWithAddressSpecification class) which is a boolean logic. Very simple, yet too powerful 😉

Moving forward, the possibilities with this pattern is quite endless and helps very much in scaling up the application. This pattern could potentially support Data-Shaping and Pagination as well. Pretty powerful pattern with a very little learning curve, yeah? That’s a wrap for this article.

Consider supporting me by buying me a coffee.

Thank you for visiting. You can now buy me a coffee by clicking the button below. Cheers!

Buy Me A Coffee

Summary

In this article, we have covered Specification Pattern in ASP.NET Core Applications and how it enhances Generic Repository Pattern by giving it an upper hand. We have also built a complete web API application that follows onion architecture for clean code management. You can also find the complete source code on my Github here. Have any suggestions or questions? Feel free to leave them in the comments section below. Thanks and Happy Coding! 😀

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

20 Comments

  1. Hello,
    How do you project to a DTO for instance or using IMapper, using specification pattern
    Thanks

    1. How do you make projections into a DTO instead fetching the entire columns using the specification pattern?

  2. The Repository Pattern with Specification Pattern , And CQRS + Event Sourcing. Interesting topic.

    By using event sourcing, all transaction are recorded, possible to rollback.

  3. Hi Mukesh,

    super interesting design pattern and great article!

    Is there a good way to combine this with the CQRS pattern?
    To me it feels more like an alternative right now, because the specification classes kind of represent what query classes are in a CQRS setup. Using both in the same project would be confusing because you could use a query or a specification to get the same result.
    Only difference I can guess right now, is that queries would return DTO’s and the specifications sent to a repository would off course return entities. However both contain business logic and I would find myself mostly writing complex specifications. Queries would then boil down to using these specifications and mapping the resulting entities to DTOs.
    I’d appreciate some advice on how to tackle this.

  4. Hello Mukesh,
    Very good article as always ;).
    Have you already tried or tested OData with .NET Core ?
    I know it is possible to combine it with CQRS.
    Could you do an article about it?
    Thanks a lot anyway, your articles inspire me a lot.
    See you later
    Jordan

  5. Where is SpecificationEvaluator defined that is in the GenericRepository implementation?
    Good article and I like the idea, but what is the difference between having a ton of different entry points with each specification into the controller, with all of the associated code and one entry point with a query object like you defined in your API course using the extensions for the repository.

    As someone who is still learning and trying hard to do the “next right thing” this can be confusing, especially when you mention time and again that a service layer between the controller and repository is a good idea, but your examples most ALWAYS leave that part out.

    I know things, they are “achanging”, but good grief, I can’t seem to keep up!

  6. I think the pattern is really intresting. But how can we add more flexible Evalutor?
    for example we want to have ‘orderby’ and then ‘thenbydescending’ and etc.

    Maybe action delegates in spec?
    What you think?

  7. Hi, Mukesh.
    Thank you for the great article.
    Could you please explain the logic in DeveloperWithAddressSpecification?
    Why do you accept int years in the constructor and compare it with EstimatedIncome?

  8. ” Now, go back to the controller, comment out line 38 and let’s use the DeveloperByIncomeSpecification for now. Again, run the application.”

    A small correction here, I guess you’re referring to line 23 of the code snippet, not the 38th line. The writeup was amazing as usual!

  9. Hi Mukesh
    thanks for your help
    but still little confused
    now if you need to search by income you create class –>DeveloperByIncomeSpecification
    if you need to search by address you create class –>DeveloperWithAddressSpecification
    Q1:
    so if i need to search by email so i need to create new classe –>Deleveloper_with_Email??
    Q2:
    if need to search by multiple conditions for example(email /address) so i need to create new class for those conditions
    Q3:
    if in some cases i need to search with mail address & in other cases search by part of mail like all developer who have mail in specific domain so i will create class for each search condition??

      1. Why complicate your life with entityframework and a lot of classes to manage advanced search condition instead of using dinamic sql string build with a simple DataFilter class to specify the query filters?

        1. Just what I was thinking… like this:
          int skip = 50;
          int take = 10;
          string rawSqlQuery = “Select * from tableX inner join… …order by tableX.lastName”
          data = _dbContext.things.FromSqlRaw(rawSqlQuery).Skip(skip).Take(take);