C0deGeek.Pagination
1.0.0
There is a newer version of this package available.
See the version list below for details.
See the version list below for details.
dotnet add package C0deGeek.Pagination --version 1.0.0
NuGet\Install-Package C0deGeek.Pagination -Version 1.0.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="C0deGeek.Pagination" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add C0deGeek.Pagination --version 1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: C0deGeek.Pagination, 1.0.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.
// Install C0deGeek.Pagination as a Cake Addin #addin nuget:?package=C0deGeek.Pagination&version=1.0.0 // Install C0deGeek.Pagination as a Cake Tool #tool nuget:?package=C0deGeek.Pagination&version=1.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
C0deGeek.Pagination
A comprehensive, production-ready pagination solution for ASP.NET Core applications with Entity Framework Core. Features include caching, rate limiting, concurrency control, and search capabilities.
Features
- ✨ Entity Framework Core integration
- 🚀 Optimized performance with caching
- 🔒 Concurrency control with RowVersion
- 🔍 Flexible search capabilities
- 🌡️ Rate limiting (both service and API level)
- 💪 Circuit breaker and retry policies
- 🔄 HATEOAS support
- 🗜️ Response compression
- 📦 ETag support for caching
- 🛡️ Thread-safe operations
Installation
dotnet add package C0deGeek.Pagination
Quick Start
- Define your entity:
public class User : IEntity
{
public int Id { get; set; }
public required string? Name { get; set; }
public required string? Email { get; set; }
public required byte[] RowVersion { get; set; }
public DateTime LastModified { get; set; }
public DateTime CreatedAt { get; set; }
public bool IsActive { get; set; }
}
- Set up your DbContext:
public class YourDbContext : PaginationDbContext<YourDbContext>
{
public DbSet<User> Users => Set<User>();
public YourDbContext(DbContextOptions<YourDbContext> options)
: base(options)
{
}
}
- Register services:
services.AddPagination<YourDbContext>(options =>
{
options.EnableCaching = true;
options.CacheSlidingExpiration = TimeSpan.FromMinutes(5);
options.EnableRateLimiting = true;
options.RateLimitPermitLimit = 100;
});
- Create your controller:
[ApiController]
[Route("api/[controller]")]
public class UsersController : PaginationControllerBase<User>
{
private readonly PaginationService<User, YourDbContext> _paginationService;
public UsersController(PaginationService<User, YourDbContext> paginationService)
{
_paginationService = paginationService;
}
[HttpGet]
[EnableRateLimiting("fixed")]
public async Task<IActionResult> GetUsers(
[FromQuery] PaginationParameters parameters,
[FromHeader(Name = "If-None-Match")] string? ifNoneMatch = null,
CancellationToken cancellationToken = default)
{
try
{
var result = await _paginationService.GetPagedDataAsync(
parameters,
cancellationToken);
if (result is null)
{
return StatusCode(304); // Not Modified
}
SetPaginationHeaders(result);
return Ok(new PaginationResponse<User>(
result.Items,
result,
GenerateLinks(result, nameof(GetUsers), new { parameters.PageSize })
));
}
catch (RateLimitExceededException)
{
return StatusCode(429, "Too many requests");
}
}
}
Configuration Options
Pagination Options
services.AddPagination<YourDbContext>(options =>
{
// Caching
options.EnableCaching = true;
options.CacheSlidingExpiration = TimeSpan.FromMinutes(5);
options.CacheAbsoluteExpiration = TimeSpan.FromHours(1);
// Rate Limiting
options.EnableRateLimiting = true;
options.RateLimitPermitLimit = 100;
options.RateLimitWindowSeconds = 1;
// Circuit Breaker
options.CircuitBreakerFailureThreshold = 5;
options.CircuitBreakerSamplingDuration = TimeSpan.FromSeconds(30);
options.CircuitBreakerDurationOfBreak = TimeSpan.FromSeconds(60);
// Retry Policy
options.RetryCount = 3;
options.RetryBaseDelayMs = 100;
// Database
options.IsolationLevel = IsolationLevel.ReadCommitted;
});
Rate Limiting Options
Choose between service-level, API-level, or both:
// Service-level only
services.AddPaginationWithServiceRateLimit<YourDbContext>(options =>
{
options.EnableRateLimiting = true;
options.RateLimitPermitLimit = 100;
});
// API-level only
services.AddPaginationWithApiRateLimit<YourDbContext>(options =>
{
options.AddFixedWindowLimiter("fixed", opt =>
{
opt.PermitLimit = 100;
opt.Window = TimeSpan.FromSeconds(1);
});
});
Search Capabilities
Option 1: Implement ISearchableEntity
public class User : IEntity, ISearchableEntity
{
// ... other properties
public bool MatchesSearchTerm(string searchTerm)
{
return (Name?.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ?? false) ||
(Email?.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ?? false);
}
}
Option 2: Create a Search Provider
public class UserSearchProvider : ISearchExpressionProvider<User>
{
public Expression<Func<User, bool>> GetSearchExpression(string searchTerm)
{
return user =>
EF.Functions.Like(user.Name, $"%{searchTerm}%") ||
EF.Functions.Like(user.Email, $"%{searchTerm}%");
}
}
// Register the provider
services.AddSearchProvider<User, UserSearchProvider>();
Response Format
The API returns responses in this format:
{
"data": [
{
"id": 1,
"name": "John Doe",
"email": "john@example.com",
"isActive": true
}
],
"pagination": {
"currentPage": 1,
"pageSize": 10,
"totalPages": 5,
"totalItems": 48,
"hasNextPage": true,
"hasPreviousPage": false,
"links": [
{
"href": "/api/users?pageNumber=1&pageSize=10",
"rel": "self",
"method": "GET"
},
{
"href": "/api/users?pageNumber=2&pageSize=10",
"rel": "next",
"method": "GET"
}
]
}
}
Response Headers
ETag
: For cache validationLast-Modified
: Last modification timestampX-Total-Count
: Total number of itemsX-Total-Pages
: Total number of pages
Error Handling
The package provides these status codes:
200 OK
: Successful request304 Not Modified
: Content hasn't changed429 Too Many Requests
: Rate limit exceeded503 Service Unavailable
: Circuit breaker open
Advanced Features
Custom Cache Key Generation
public class CustomCacheKeyProvider : ICacheKeyProvider<User>
{
public string GetCacheKey(PaginationParameters parameters)
{
return $"custom_key_{parameters.GetHashCode()}";
}
}
services.AddScoped<ICacheKeyProvider<User>, CustomCacheKeyProvider>();
Custom Sort Expressions
public class UserSortProvider : ISortExpressionProvider<User>
{
public IQueryable<User> ApplySort(
IQueryable<User> query,
string sortBy,
bool descending)
{
// Custom sorting logic
}
}
services.AddScoped<ISortExpressionProvider<User>, UserSortProvider>();
Best Practices
- Always use cancellation tokens for async operations
- Implement proper error handling
- Set appropriate cache durations
- Configure rate limits based on your API's capacity
- Monitor circuit breaker events
- Use appropriate isolation levels for your use case
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature
) - Commit your changes (
git commit -m 'Add some AmazingFeature'
) - Push to the branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE.md file for details
Acknowledgments
- Built with ASP.NET Core and Entity Framework Core
- Uses Polly for resilience patterns
- Inspired by REST best practices and HATEOAS principles
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
-
net8.0
- Microsoft.AspNetCore.Http.Abstractions (>= 2.2.0)
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Microsoft.EntityFrameworkCore.SqlServer (>= 8.0.10)
- Microsoft.Extensions.Caching.Memory (>= 8.0.1)
- Microsoft.Extensions.DependencyInjection (>= 8.0.1)
- Polly (>= 8.4.2)
- System.Threading.RateLimiting (>= 8.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.