DKNet.EfCore.Specifications 9.5.37

There is a newer version of this package available.
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
                    
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="DKNet.EfCore.Specifications" Version="9.5.37" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DKNet.EfCore.Specifications" Version="9.5.37" />
                    
Directory.Packages.props
<PackageReference Include="DKNet.EfCore.Specifications" />
                    
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 DKNet.EfCore.Specifications --version 9.5.37
                    
#r "nuget: DKNet.EfCore.Specifications, 9.5.37"
                    
#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 DKNet.EfCore.Specifications@9.5.37
                    
#: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=DKNet.EfCore.Specifications&version=9.5.37
                    
Install as a Cake Addin
#tool nuget:?package=DKNet.EfCore.Specifications&version=9.5.37
                    
Install as a Cake Tool

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

  1. Reusability: Create named specification classes for common query patterns
  2. Composition: Build complex queries by combining simple specifications
  3. Type Safety: Prefer static predicates when possible; use dynamic queries for runtime flexibility
  4. Performance: Use AddInclude() wisely to avoid N+1 queries
  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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