TwoRivers.Specifications.EntityFrameworkCore
2.0.0-beta.1
See the version list below for details.
dotnet add package TwoRivers.Specifications.EntityFrameworkCore --version 2.0.0-beta.1
NuGet\Install-Package TwoRivers.Specifications.EntityFrameworkCore -Version 2.0.0-beta.1
<PackageReference Include="TwoRivers.Specifications.EntityFrameworkCore" Version="2.0.0-beta.1" />
<PackageVersion Include="TwoRivers.Specifications.EntityFrameworkCore" Version="2.0.0-beta.1" />
<PackageReference Include="TwoRivers.Specifications.EntityFrameworkCore" />
paket add TwoRivers.Specifications.EntityFrameworkCore --version 2.0.0-beta.1
#r "nuget: TwoRivers.Specifications.EntityFrameworkCore, 2.0.0-beta.1"
#:package TwoRivers.Specifications.EntityFrameworkCore@2.0.0-beta.1
#addin nuget:?package=TwoRivers.Specifications.EntityFrameworkCore&version=2.0.0-beta.1&prerelease
#tool nuget:?package=TwoRivers.Specifications.EntityFrameworkCore&version=2.0.0-beta.1&prerelease
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 supportSpecificationEvaluator<TEntity>- Converts specifications to EF Core queryable expressionsISpecificationRepository<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 toIQueryable<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 | Versions 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. |
-
net10.0
- Microsoft.EntityFrameworkCore (>= 10.0.3)
- TwoRivers.Specifications (>= 2.0.0-beta.1)
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 |