DKNet.EfCore.Specifications
9.5.37
See the version list below for details.
dotnet add package DKNet.EfCore.Specifications --version 9.5.37
NuGet\Install-Package DKNet.EfCore.Specifications -Version 9.5.37
<PackageReference Include="DKNet.EfCore.Specifications" Version="9.5.37" />
<PackageVersion Include="DKNet.EfCore.Specifications" Version="9.5.37" />
<PackageReference Include="DKNet.EfCore.Specifications" />
paket add DKNet.EfCore.Specifications --version 9.5.37
#r "nuget: DKNet.EfCore.Specifications, 9.5.37"
#:package DKNet.EfCore.Specifications@9.5.37
#addin nuget:?package=DKNet.EfCore.Specifications&version=9.5.37
#tool nuget:?package=DKNet.EfCore.Specifications&version=9.5.37
DKNet.EfCore.Specifications
A powerful and flexible specification pattern implementation for Entity Framework Core with dynamic LINQ query support.
Features
- 🎯 Specification Pattern - Build reusable, composable query logic
- ⚡ Dynamic LINQ Queries - Runtime query construction using string expressions
- 🔗 Fluent API - Chainable methods for building complex queries
- 🛠️ Type-Safe Operations - Strongly-typed filter operations
- 📊 LinqKit Integration - Combine static and dynamic predicates seamlessly
Installation
dotnet add package DKNet.EfCore.Specifications
Quick Start
1. Basic Specification Usage
public class ActivePersonsSpec : Specification<Person>
{
public ActivePersonsSpec()
{
WithFilter(p => p.IsActive && !p.IsDeleted);
AddInclude(p => p.Address);
AddOrderBy(p => p.Name);
}
}
// Usage
var spec = new ActivePersonsSpec();
var persons = await repository.ListAsync(spec);
2. Dynamic Filter Queries
public class PersonSearchSpec : Specification<Person>
{
public PersonSearchSpec(int minAge, string nameContains)
{
// Add dynamic filters
AddFilter("Age", FilterOperations.GreaterThanOrEqual, minAge);
AddFilter("Name", FilterOperations.Contains, nameContains);
// Mix with static filters
WithFilter(p => !p.IsDeleted);
}
}
// Usage
var spec = new PersonSearchSpec(18, "John");
var results = await repository.ListAsync(spec);
3. Dynamic Predicate Builder
The DynamicPredicateBuilder provides a fluent API for building complex dynamic queries:
var builder = new DynamicPredicateBuilder()
.With("Age", Operation.GreaterThan, 18)
.With("Department.Name", Operation.Equal, "Engineering")
.With("Salary", Operation.GreaterThanOrEqual, 50000)
.With("Name", Operation.Contains, "Smith");
var predicate = builder.Build(out var values);
// Result: "Age > @0 and Department.Name == @1 and Salary >= @2 and Name.Contains(@3)"
// values: [18, "Engineering", 50000, "Smith"]
// Use with EF Core
var employees = await context.Employees
.Where(predicate, values)
.ToListAsync();
4. Dynamic LINQ Extensions
Combine static predicates with dynamic expressions using LinqKit:
var predicate = PredicateBuilder.New<Person>()
.And(p => p.IsActive)
.And("Age > @0 and Salary >= @1", 25, 60000)
.Or("Department == @0", "Executive");
// Result: (IsActive AND (Age > 25 and Salary >= 60000)) OR (Department == "Executive")
var query = context.Persons.Where(predicate);
Available Operations
The Operation enum supports the following filter operations:
| Operation | Description | Example |
|---|---|---|
Equal |
Equality comparison (==) | "Age == @0" |
NotEqual |
Inequality comparison (!=) | "Status != @0" |
GreaterThan |
Greater than (>) | "Salary > @0" |
GreaterThanOrEqual |
Greater than or equal (>=) | "Age >= @0" |
LessThan |
Less than (<) | "Price < @0" |
LessThanOrEqual |
Less than or equal (<=) | "Score <= @0" |
Contains |
String contains | "Name.Contains(@0)" |
NotContains |
String does not contain | "!Name.Contains(@0)" |
StartsWith |
String starts with | "Name.StartsWith(@0)" |
EndsWith |
String ends with | "Name.EndsWith(@0)" |
Any |
Collection any | "Tags.Any(x => x == @0)" |
All |
Collection all | "Tags.All(x => x == @0)" |
Advanced Scenarios
API Controller with Dynamic Queries
[HttpGet]
public async Task<IActionResult> SearchEmployees(
[FromQuery] string? filter,
[FromQuery] string? orderBy,
[FromQuery] int minSalary = 0)
{
var spec = new Specification<Employee>();
// Always filter deleted records
spec.WithFilter(e => !e.IsDeleted);
// Add dynamic filters from query string
if (!string.IsNullOrEmpty(filter))
{
var builder = new DynamicPredicateBuilder()
.With("Salary", Operation.GreaterThanOrEqual, minSalary);
var predicate = builder.Build(out var values);
// Apply to specification using the extension method
}
// Dynamic ordering
if (!string.IsNullOrEmpty(orderBy))
{
spec.WithOrderBy(orderBy, isDescending: false);
}
var employees = await _repository.ListAsync(spec);
return Ok(employees);
}
Nested Property Filtering
var builder = new DynamicPredicateBuilder()
.With("Address.City", Operation.Equal, "New York")
.With("Department.Manager.Name", Operation.StartsWith, "John")
.With("Projects.Any(x => x.IsActive)", Operation.Equal, true);
var predicate = builder.Build(out var values);
Combining Multiple Specifications
public class EmployeeSearchSpec : Specification<Employee>
{
public EmployeeSearchSpec(
string? department = null,
int? minAge = null,
decimal? minSalary = null)
{
// Base filter
WithFilter(e => e.IsActive);
// Dynamic filters
if (!string.IsNullOrEmpty(department))
{
AddFilter("Department.Name", FilterOperations.Equal, department);
}
if (minAge.HasValue)
{
AddFilter("Age", FilterOperations.GreaterThanOrEqual, minAge.Value);
}
if (minSalary.HasValue)
{
AddFilter("Salary", FilterOperations.GreaterThanOrEqual, minSalary.Value);
}
// Includes
AddInclude(e => e.Department);
AddInclude(e => e.Address);
// Ordering
AddOrderBy(e => e.Department);
AddOrderByDescending(e => e.Salary);
}
}
Best Practices
- Reusability: Create named specification classes for common query patterns
- Composition: Build complex queries by combining simple specifications
- Type Safety: Prefer static predicates when possible; use dynamic queries for runtime flexibility
- Performance: Use
AddInclude()wisely to avoid N+1 queries - Security: Validate and sanitize user input before using in dynamic queries
Requirements
- .NET 9.0 or higher
- Entity Framework Core 9.0 or higher
- LinqKit.Microsoft.EntityFrameworkCore
- System.Linq.Dynamic.Core
License
Licensed under the MIT License. See LICENSE in the project root for license information.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net9.0
- DKNet.EfCore.Extensions (>= 9.5.37)
- LinqKit.Microsoft.EntityFrameworkCore (>= 9.0.8)
- Mapster (>= 7.4.0)
- Microsoft.EntityFrameworkCore (>= 9.0.10)
- Microsoft.EntityFrameworkCore.DynamicLinq (>= 9.6.9)
- System.Linq.Dynamic.Core (>= 1.6.9)
- X.PagedList.EF (>= 10.5.9)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DKNet.EfCore.Specifications:
| Package | Downloads |
|---|---|
|
DKNet.EfCore.Repos
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 10.0.3 | 129 | 11/21/2025 |
| 10.0.2 | 175 | 11/20/2025 |
| 9.5.40 | 169 | 11/19/2025 |
| 9.5.39 | 319 | 11/13/2025 |
| 9.5.38 | 228 | 11/6/2025 |
| 9.5.37 | 206 | 11/5/2025 |
| 9.5.36 | 209 | 11/5/2025 |
| 9.5.35 | 207 | 11/4/2025 |
| 9.5.34 | 200 | 11/4/2025 |
| 9.5.33 | 208 | 11/3/2025 |
| 9.5.32 | 207 | 11/3/2025 |
| 9.5.31 | 179 | 10/31/2025 |
| 9.5.30 | 201 | 10/31/2025 |
| 9.5.29 | 213 | 10/30/2025 |
| 9.5.28 | 203 | 10/27/2025 |
| 9.5.27 | 217 | 10/27/2025 |
| 9.5.26 | 203 | 10/27/2025 |
| 9.5.25 | 190 | 10/26/2025 |
| 9.5.24 | 95 | 10/25/2025 |
| 9.5.23 | 88 | 10/25/2025 |
| 9.5.22 | 97 | 10/25/2025 |
| 9.5.21 | 157 | 10/24/2025 |
| 9.5.20 | 174 | 10/23/2025 |
| 9.5.19 | 157 | 10/23/2025 |
| 9.5.18 | 161 | 10/22/2025 |
| 9.5.17 | 190 | 10/17/2025 |
| 9.5.16 | 136 | 10/17/2025 |
| 9.5.15 | 176 | 10/15/2025 |
| 9.5.14 | 175 | 10/14/2025 |
| 9.5.13 | 161 | 10/14/2025 |
| 9.5.12 | 161 | 10/14/2025 |
| 9.5.11 | 165 | 10/14/2025 |
| 9.5.10 | 184 | 10/14/2025 |
| 9.5.9 | 161 | 10/13/2025 |
| 9.5.8 | 102 | 10/11/2025 |
| 9.5.7 | 114 | 10/10/2025 |
| 9.5.6 | 117 | 10/10/2025 |
| 9.5.5 | 121 | 10/10/2025 |
| 9.5.4 | 131 | 10/10/2025 |
| 9.5.3 | 195 | 10/8/2025 |
| 9.5.2 | 158 | 10/8/2025 |
| 9.5.1 | 178 | 10/7/2025 |
| 9.0.42 | 165 | 10/6/2025 |
| 9.0.41 | 173 | 10/2/2025 |
| 9.0.40 | 140 | 9/27/2025 |
| 9.0.39 | 148 | 9/26/2025 |
| 9.0.38 | 175 | 9/24/2025 |
| 9.0.37 | 156 | 9/23/2025 |
| 9.0.36 | 187 | 9/23/2025 |
| 9.0.35 | 171 | 9/23/2025 |
| 9.0.34 | 175 | 9/23/2025 |
| 9.0.33 | 157 | 9/21/2025 |
| 9.0.32 | 156 | 9/21/2025 |
| 9.0.31 | 288 | 9/19/2025 |
| 9.0.30 | 291 | 9/18/2025 |
| 9.0.29 | 281 | 9/18/2025 |
| 9.0.28 | 303 | 9/17/2025 |
| 9.0.27 | 295 | 9/17/2025 |
| 9.0.26 | 300 | 9/16/2025 |
| 9.0.25 | 243 | 9/15/2025 |
| 9.0.24 | 250 | 9/15/2025 |
| 0.0.1 | 171 | 11/19/2025 |