TwoRivers.Specifications.EntityFrameworkCore 2.0.0-beta.0

This is a prerelease version of TwoRivers.Specifications.EntityFrameworkCore.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package TwoRivers.Specifications.EntityFrameworkCore --version 2.0.0-beta.0
                    
NuGet\Install-Package TwoRivers.Specifications.EntityFrameworkCore -Version 2.0.0-beta.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TwoRivers.Specifications.EntityFrameworkCore" Version="2.0.0-beta.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TwoRivers.Specifications.EntityFrameworkCore" Version="2.0.0-beta.0" />
                    
Directory.Packages.props
<PackageReference Include="TwoRivers.Specifications.EntityFrameworkCore" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TwoRivers.Specifications.EntityFrameworkCore --version 2.0.0-beta.0
                    
#r "nuget: TwoRivers.Specifications.EntityFrameworkCore, 2.0.0-beta.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TwoRivers.Specifications.EntityFrameworkCore@2.0.0-beta.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TwoRivers.Specifications.EntityFrameworkCore&version=2.0.0-beta.0&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=TwoRivers.Specifications.EntityFrameworkCore&version=2.0.0-beta.0&prerelease
                    
Install as a Cake Tool

TwoRivers.Specifications.EntityFrameworkCore

The TwoRivers.Specifications.EntityFrameworkCore package provides Entity Framework Core integration for the Specification Pattern, allowing you to encapsulate query logic and apply it to DbContext queries efficiently.

Installation

You can install the TwoRivers.Specifications.EntityFrameworkCore package via NuGet Package Manager:

Install-Package TwoRivers.Specifications.EntityFrameworkCore

Or via the .NET CLI:

dotnet add package TwoRivers.Specifications.EntityFrameworkCore

Overview

This package bridges the TwoRivers.Specifications pattern with Entity Framework Core, providing:

  • SpecificationRepository<TEntity, TDbContext> - Generic repository with specification support
  • SpecificationEvaluator<TEntity> - Converts specifications to EF Core queryable expressions
  • ISpecificationRepository<TEntity> - Repository abstraction for specification queries

Features

SpecificationRepository

A generic repository implementation that applies specifications to Entity Framework Core queries:

  • Supports all CRUD operations
  • Applies specifications to IQueryable
  • Handles async operations with cancellation tokens
  • Implements proper dispose pattern

SpecificationEvaluator

Evaluates specifications and applies them to EF Core queryables:

  • Converts IQuery<T> specifications to IQueryable<T>
  • Handles Where, OrderBy, Skip, Take operations
  • Optimizes query execution with EF Core

Usage

Basic Repository Usage

using Microsoft.EntityFrameworkCore;
using TwoRivers.Specifications;

public class ApplicationDbContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }
}

// Using the repository
public class CustomerService
{
    private readonly SpecificationRepository<Customer, ApplicationDbContext> _repository;

    public CustomerService(ApplicationDbContext context)
    {
        _repository = new SpecificationRepository<Customer, ApplicationDbContext>(context);
    }

    public async Task<IEnumerable<Customer>> GetActiveCustomersAsync()
    {
        var spec = new ActiveCustomersSpecification();
        return await _repository.FindAsync(spec).ToListAsync();
    }

    public async Task<Customer?> GetCustomerByIdAsync(Guid id)
    {
        var spec = new CustomerByIdSpecification(id);
        return await _repository.GetAsync(spec);
    }

    public async Task<Int32> CountActiveCustomersAsync()
    {
        var spec = new ActiveCustomersSpecification();
        return await _repository.CountAsync(spec);
    }
}

Dependency Injection Registration

using Microsoft.Extensions.DependencyInjection;
using TwoRivers.Specifications;

services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

// Register repository
services.AddScoped<ISpecificationRepository<Customer>, 
    SpecificationRepository<Customer, ApplicationDbContext>>();

// Or use the extension method (if available)
services.AddSpecificationRepository<Customer, ApplicationDbContext>();

Complete Example with Specifications

using TwoRivers.Specifications;

// Define your entity
public class Customer
{
    public Guid Id { get; set; }
    public String Name { get; set; }
    public String Email { get; set; }
    public Boolean IsActive { get; set; }
    public DateTime CreatedAt { get; set; }
}

// Create specifications
public class ActiveCustomersSpecification : BaseQuery<Customer>
{
    public ActiveCustomersSpecification()
    {
        AddCriteria(c => c.IsActive);
        ApplyOrderBy(c => c.Name);
    }
}

public class CustomerByEmailSpecification : BaseQuery<Customer>
{
    public CustomerByEmailSpecification(String email)
    {
        AddCriteria(c => c.Email == email);
    }
}

public class RecentCustomersSpecification : BaseQuery<Customer>
{
    public RecentCustomersSpecification(Int32 days, Int32 pageSize = 10)
    {
        var cutoffDate = DateTime.UtcNow.AddDays(-days);
        AddCriteria(c => c.CreatedAt >= cutoffDate);
        ApplyOrderByDescending(c => c.CreatedAt);
        ApplyPaging(0, pageSize);
    }
}

// Use in a service
public class CustomerQueryService
{
    private readonly ISpecificationRepository<Customer> _repository;

    public CustomerQueryService(ISpecificationRepository<Customer> repository)
    {
        _repository = repository;
    }

    public async Task<IEnumerable<Customer>> GetActiveCustomersAsync()
    {
        var spec = new ActiveCustomersSpecification();
        return await _repository.FindAsync(spec).ToListAsync();
    }

    public async Task<Boolean> IsEmailInUseAsync(String email)
    {
        var spec = new CustomerByEmailSpecification(email);
        return await _repository.AnyAsync(spec);
    }

    public async Task<IEnumerable<Customer>> GetRecentCustomersAsync(Int32 days = 30)
    {
        var spec = new RecentCustomersSpecification(days, pageSize: 20);
        return await _repository.FindAsync(spec).ToListAsync();
    }
}

Direct Specification Evaluation

If you prefer not to use the repository pattern:

public class CustomerService
{
    private readonly ApplicationDbContext _context;

    public CustomerService(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<List<Customer>> GetActiveCustomersAsync()
    {
        var spec = new ActiveCustomersSpecification();
        var query = SpecificationEvaluator<Customer>.GetQuery(
            _context.Customers.AsQueryable(), 
            spec);

        return await query.ToListAsync();
    }
}

Best Practices

Keep Specifications Focused

Each specification should represent a single query concept:

// Good - single responsibility
public class ActiveCustomersSpecification : BaseQuery<Customer>
{
    public ActiveCustomersSpecification()
    {
        AddCriteria(c => c.IsActive);
    }
}

// Good - composable
var spec = new ActiveCustomersSpecification()
    .And(new CustomersByStateSpecification("CA"))
    .OrderBy(c => c.Name);

Use Strongly-Typed Specifications

Avoid stringly-typed queries:

// Good
var spec = new CustomerByEmailSpecification(customer.Email);

// Avoid
var query = _context.Customers.Where($"Email == '{customer.Email}'");

Leverage Async Methods

Always use async methods for database queries:

// Good
var customers = await _repository.FindAsync(spec).ToListAsync();

// Avoid blocking
var customers = _repository.FindAsync(spec).ToEnumerable().ToList();

License

This project is licensed under the MIT License. See the LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0-beta.8 73 3/5/2026
2.0.0-beta.7 63 3/5/2026
2.0.0-beta.6 62 3/5/2026
2.0.0-beta.5 62 3/4/2026
2.0.0-beta.4 65 3/4/2026
2.0.0-beta.3 62 3/3/2026
2.0.0-beta.2 66 3/3/2026
2.0.0-beta.1 62 3/3/2026
2.0.0-beta.0 64 3/3/2026
1.1.6 120 2/26/2026
1.1.5 106 2/25/2026
1.1.4 115 2/22/2026
1.1.3 105 2/22/2026
1.1.2 114 2/22/2026
1.1.1 116 2/22/2026
1.1.0 109 2/22/2026
1.0.0 138 2/17/2026
Loading failed