DKNet.EfCore.Specifications
9.5.25
See the version list below for details.
dotnet add package DKNet.EfCore.Specifications --version 9.5.25
NuGet\Install-Package DKNet.EfCore.Specifications -Version 9.5.25
<PackageReference Include="DKNet.EfCore.Specifications" Version="9.5.25" />
<PackageVersion Include="DKNet.EfCore.Specifications" Version="9.5.25" />
<PackageReference Include="DKNet.EfCore.Specifications" />
paket add DKNet.EfCore.Specifications --version 9.5.25
#r "nuget: DKNet.EfCore.Specifications, 9.5.25"
#:package DKNet.EfCore.Specifications@9.5.25
#addin nuget:?package=DKNet.EfCore.Specifications&version=9.5.25
#tool nuget:?package=DKNet.EfCore.Specifications&version=9.5.25
DKNet.EfCore.Specifications
A flexible and reusable implementation of the Specification Pattern for Entity Framework Core, providing clean data access without the overhead of traditional repository patterns.
Overview
This library provides a powerful way to build flexible and reusable database queries in .NET projects. Define reusable filters, includes, and sorting as specification classes, avoiding the problems that come with large, hard-to-maintain repositories.
Features
- Specification Pattern: Build complex queries using composable specification objects
- Repository Pattern: Optional
IRepositorySpecinterface for common CRUD operations - Type-Safe Queries: Strongly-typed query specifications with IntelliSense support
- Flexible Filtering: Define reusable filter criteria
- Include Support: Eagerly load related entities
- Sorting: Support for both ascending and descending ordering
- Query Filter Control: Ability to ignore global query filters (e.g., soft delete, multi-tenancy)
- Projection Support: Map entities to DTOs using Mapster integration
Installation
dotnet add package DKNet.EfCore.Specifications
Core Components
ISpecification<TEntity>
The base specification interface that defines:
FilterQuery: Filtering function to test each element for a conditionIncludeQueries: Collection of functions describing included entitiesOrderByQueries: Functions for ascending orderingOrderByDescendingQueries: Functions for descending orderingIgnoreQueryFilters: Flag to ignore global query filters
Specification<TEntity>
Abstract base class for creating custom specifications:
public class ActiveUsersSpecification : Specification<User>
{
public ActiveUsersSpecification()
{
WithFilter(u => u.IsActive);
AddInclude(u => u.Profile);
AddOrderBy(u => u.LastName);
}
}
IRepositorySpec
Interface for repository operations with specification support:
Query Operations
Query<TEntity>(spec): Query entities using a specificationQuery<TEntity, TModel>(spec): Query and project to a model type
Transaction Management
BeginTransactionAsync(): Start a new database transactionEntry<TEntity>(entity): Access entity change tracking information
CRUD Operations
AddAsync<TEntity>(entity): Add a single entityAddRangeAsync<TEntity>(entities): Add multiple entitiesUpdateAsync<TEntity>(entity): Update an entity and handle navigation propertiesUpdateRangeAsync<TEntity>(entities): Update multiple entitiesDelete<TEntity>(entity): Mark entity for deletionDeleteRange<TEntity>(entities): Mark multiple entities for deletionSaveChangesAsync(): Persist all changes to the database
RepositorySpec<TDbContext>
Concrete implementation of IRepositorySpec with:
- Automatic handling of new entities from navigation properties during updates
- Mapster integration for entity-to-DTO projections
- Support for query filters and change tracking
Usage Examples
Creating a Specification
public class UsersByRoleSpecification : Specification<User>
{
public UsersByRoleSpecification(string role)
{
// Define filter criteria
WithFilter(u => u.Role == role && u.IsActive);
// Include related entities
AddInclude(u => u.Profile);
AddInclude(u => u.Orders);
// Define ordering
AddOrderBy(u => u.LastName);
AddOrderBy(u => u.FirstName);
}
}
Using Specifications with IRepositorySpec
public class UserService
{
private readonly IRepositorySpec _repository;
public UserService(IRepositorySpec repository)
{
_repository = repository;
}
public async Task<List<User>> GetActiveAdmins()
{
var spec = new UsersByRoleSpecification("Admin");
return await _repository.Query(spec).ToListAsync();
}
public async Task<List<UserDto>> GetActiveAdminsAsDto()
{
var spec = new UsersByRoleSpecification("Admin");
return await _repository.Query<User, UserDto>(spec).ToListAsync();
}
}
CRUD Operations
// Add a new entity
var user = new User { FirstName = "John", LastName = "Doe" };
await _repository.AddAsync(user);
await _repository.SaveChangesAsync();
// Update an entity
user.IsActive = false;
await _repository.UpdateAsync(user);
await _repository.SaveChangesAsync();
// Delete an entity
_repository.Delete(user);
await _repository.SaveChangesAsync();
// Transaction support
using var transaction = await _repository.BeginTransactionAsync();
try
{
await _repository.AddAsync(newUser);
await _repository.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
Ignoring Query Filters
public class AllUsersIncludingSoftDeletedSpecification : Specification<User>
{
public AllUsersIncludingSoftDeletedSpecification()
{
IgnoreQueryFiltersEnabled();
AddOrderBy(u => u.CreatedDate);
}
}
Complex Filtering with PredicateBuilder
public class DynamicUserSearchSpecification : Specification<User>
{
public DynamicUserSearchSpecification(string? name, bool? isActive)
{
var predicate = CreatePredicate();
if (!string.IsNullOrEmpty(name))
predicate = predicate.And(u => u.FirstName.Contains(name) || u.LastName.Contains(name));
if (isActive.HasValue)
predicate = predicate.And(u => u.IsActive == isActive.Value);
WithFilter(predicate);
}
}
Dependency Injection Setup
services.AddDbContext<MyDbContext>(options =>
options.UseNpgsql(connectionString));
services.AddScoped<IRepositorySpec, RepositorySpec<MyDbContext>>();
// For projection support, register Mapster
services.AddSingleton<IMapper>(new Mapper());
Benefits
- Reusability: Define query logic once, use it everywhere
- Testability: Easy to unit test specifications in isolation
- Maintainability: Query logic is centralized and versioned
- Type Safety: Compile-time checking of query expressions
- Separation of Concerns: Business logic separate from data access
- Performance: Optimized query execution with proper includes and filtering
Migration Notes
Breaking Changes in Latest Version:
AndSpecificationandOrSpecificationclasses have been removed- Use LinqKit's
PredicateBuilderfor combining filter expressions instead - All methods in
IRepositorySpecare now properly documented with XML comments
References
- Original blog post: https://antondevtips.com/blog/specification-pattern-in-ef-core-flexible-data-access-without-repositories
- LinqKit Documentation: https://github.com/scottksmith95/LINQKit
- Mapster Documentation: https://github.com/MapsterMapper/Mapster
License
This library is part of the DKNet.FW framework.
| 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.25)
- LinqKit.Microsoft.EntityFrameworkCore (>= 9.0.8)
- Mapster (>= 7.4.0)
- Microsoft.EntityFrameworkCore (>= 9.0.10)
- 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 |
|---|---|---|
| 9.5.28 | 28 | 10/27/2025 |
| 9.5.27 | 33 | 10/27/2025 |
| 9.5.26 | 35 | 10/27/2025 |
| 9.5.25 | 32 | 10/26/2025 |
| 9.5.24 | 33 | 10/25/2025 |
| 9.5.23 | 30 | 10/25/2025 |
| 9.5.22 | 41 | 10/25/2025 |
| 9.5.21 | 99 | 10/24/2025 |
| 9.5.20 | 117 | 10/23/2025 |
| 9.5.19 | 103 | 10/23/2025 |
| 9.5.18 | 108 | 10/22/2025 |
| 9.5.17 | 184 | 10/17/2025 |
| 9.5.16 | 130 | 10/17/2025 |
| 9.5.15 | 171 | 10/15/2025 |
| 9.5.14 | 170 | 10/14/2025 |
| 9.5.13 | 156 | 10/14/2025 |
| 9.5.12 | 156 | 10/14/2025 |
| 9.5.11 | 160 | 10/14/2025 |
| 9.5.10 | 179 | 10/14/2025 |
| 9.5.9 | 157 | 10/13/2025 |
| 9.5.8 | 98 | 10/11/2025 |
| 9.5.7 | 109 | 10/10/2025 |
| 9.5.6 | 112 | 10/10/2025 |
| 9.5.5 | 117 | 10/10/2025 |
| 9.5.4 | 126 | 10/10/2025 |
| 9.5.3 | 190 | 10/8/2025 |
| 9.5.2 | 153 | 10/8/2025 |
| 9.5.1 | 173 | 10/7/2025 |
| 9.0.42 | 161 | 10/6/2025 |
| 9.0.41 | 168 | 10/2/2025 |
| 9.0.40 | 136 | 9/27/2025 |
| 9.0.39 | 144 | 9/26/2025 |
| 9.0.38 | 170 | 9/24/2025 |
| 9.0.37 | 152 | 9/23/2025 |
| 9.0.36 | 183 | 9/23/2025 |
| 9.0.35 | 167 | 9/23/2025 |
| 9.0.34 | 171 | 9/23/2025 |
| 9.0.33 | 153 | 9/21/2025 |
| 9.0.32 | 153 | 9/21/2025 |
| 9.0.31 | 283 | 9/19/2025 |
| 9.0.30 | 287 | 9/18/2025 |
| 9.0.29 | 278 | 9/18/2025 |
| 9.0.28 | 299 | 9/17/2025 |
| 9.0.27 | 290 | 9/17/2025 |
| 9.0.26 | 287 | 9/16/2025 |
| 9.0.25 | 229 | 9/15/2025 |
| 9.0.24 | 235 | 9/15/2025 |