OakIdeas.GenericRepository
0.0.7-alpha
dotnet add package OakIdeas.GenericRepository --version 0.0.7-alpha
NuGet\Install-Package OakIdeas.GenericRepository -Version 0.0.7-alpha
<PackageReference Include="OakIdeas.GenericRepository" Version="0.0.7-alpha" />
<PackageVersion Include="OakIdeas.GenericRepository" Version="0.0.7-alpha" />
<PackageReference Include="OakIdeas.GenericRepository" />
paket add OakIdeas.GenericRepository --version 0.0.7-alpha
#r "nuget: OakIdeas.GenericRepository, 0.0.7-alpha"
#:package OakIdeas.GenericRepository@0.0.7-alpha
#addin nuget:?package=OakIdeas.GenericRepository&version=0.0.7-alpha&prerelease
#tool nuget:?package=OakIdeas.GenericRepository&version=0.0.7-alpha&prerelease
GenericRepository
A versatile and extensible implementation of the repository pattern for CRUD operations with support for middleware pipelines, multiple storage backends, and advanced querying.
Features
- Generic Repository Interface - Type-safe repository operations with support for custom key types
- Multiple Implementations:
- In-Memory Repository (Dictionary-based, perfect for testing)
- Entity Framework Core Repository
- Middleware Pipeline - Extensible middleware architecture for cross-cutting concerns
- Soft Delete Support - Built-in soft delete functionality
- Query Object Pattern - Fluent API for building complex queries
- Specification Pattern - Reusable business rules and query logic
- Async Enumerable Support - Stream large datasets without loading everything into memory
- Type-Safe Navigation - Type-safe eager loading of related entities
Packages
- OakIdeas.GenericRepository - Core interfaces and base functionality
- OakIdeas.GenericRepository.Memory - In-memory repository implementation
- OakIdeas.GenericRepository.EntityFrameworkCore - EF Core repository implementation
- OakIdeas.GenericRepository.Middleware - Middleware infrastructure and standard middleware
Basic Usage
using OakIdeas.GenericRepository;
using OakIdeas.GenericRepository.Models;
public class Customer : EntityBase
{
public string Name { get; set; }
public string Email { get; set; }
}
// Create a repository instance
var repository = new MemoryGenericRepository<Customer>();
// Create (C)
var customer = await repository.Insert(new()
{
Name = "John Doe",
Email = "john@example.com"
});
// Retrieve (R)
var foundCustomer = await repository.Get(customer.ID);
// Or retrieve with filtering
var customers = await repository.Get(
filter: c => c.Name.Contains("John"),
orderBy: q => q.OrderBy(c => c.Name)
);
// Update (U)
customer.Name = "Jane Doe";
await repository.Update(customer);
// Delete (D)
await repository.Delete(customer.ID);
Middleware Architecture
The middleware system provides a powerful way to add cross-cutting concerns to your repository operations.
Configuring Options
using OakIdeas.GenericRepository.Core;
using OakIdeas.GenericRepository.Middleware.Extensions;
// Create options with middleware support
var options = new RepositoryOptions()
.WithLogging(enabled: true)
.WithPerformanceTracking(enabled: true)
.WithValidation(enabled: true);
var repository = new MemoryGenericRepository<Customer>(options);
Standard Middleware
Logging Middleware
Automatically logs repository operations:
using OakIdeas.GenericRepository.Middleware;
using OakIdeas.GenericRepository.Middleware.Standard;
var pipeline = new MiddlewarePipeline<Customer, int>();
pipeline.Use(new LoggingMiddleware<Customer, int>(
logger: message => Console.WriteLine(message),
includeTimings: true
));
Validation Middleware
Validates entities before operations:
var validationMiddleware = new ValidationMiddleware<Customer, int>(
validator: customer =>
{
if (string.IsNullOrEmpty(customer.Email))
return (false, "Email is required");
if (!customer.Email.Contains("@"))
return (false, "Invalid email format");
return (true, null);
}
);
pipeline.Use(validationMiddleware);
Performance Middleware
Tracks operation performance:
var performanceMiddleware = new PerformanceMiddleware<Customer, int>(
metricCollector: (operationName, elapsedMs) =>
{
Console.WriteLine($"{operationName} took {elapsedMs}ms");
},
warningThresholdMs: 1000 // Warn if operation takes more than 1 second
);
pipeline.Use(performanceMiddleware);
Custom Middleware
Create your own middleware by implementing IRepositoryMiddleware<TEntity, TKey>:
public class AuditMiddleware<TEntity, TKey> : IRepositoryMiddleware<TEntity, TKey>
where TEntity : class
where TKey : notnull
{
private readonly IAuditLogger _auditLogger;
public AuditMiddleware(IAuditLogger auditLogger)
{
_auditLogger = auditLogger;
}
public async Task InvokeAsync(
RepositoryContext<TEntity, TKey> context,
RepositoryMiddlewareDelegate<TEntity, TKey> next)
{
// Before operation
_auditLogger.LogBefore(context.Operation, context.Entity);
await next(context);
// After operation
_auditLogger.LogAfter(context.Operation, context.Result, context.Success);
}
}
Advanced Querying
Query Object Pattern
using OakIdeas.GenericRepository;
var query = new Query<Customer>()
.Where(c => c.Name.StartsWith("J"))
.Sort(q => q.OrderByDescending(c => c.ID))
.Include(c => c.Orders)
.Paged(page: 1, pageSize: 10)
.WithNoTracking();
var results = await repository.Get(query);
Specification Pattern
using OakIdeas.GenericRepository.Specifications;
public class ActiveCustomerSpecification : Specification<Customer>
{
public override Expression<Func<Customer, bool>> ToExpression()
{
return customer => customer.IsActive && !customer.IsDeleted;
}
}
public class CustomerByNameSpecification : Specification<Customer>
{
private readonly string _name;
public CustomerByNameSpecification(string name)
{
_name = name;
}
public override Expression<Func<Customer, bool>> ToExpression()
{
return customer => customer.Name.Contains(_name);
}
}
// Use specifications
var activeCustomers = await repository.Get(
filter: new ActiveCustomerSpecification()
);
// Combine specifications
var activeJohns = new ActiveCustomerSpecification()
.And(new()ByNameSpecification("John"));
var results = await repository.Get(filter: activeJohns);
Soft Delete Support
using OakIdeas.GenericRepository;
using OakIdeas.GenericRepository.Models;
public class Customer : SoftDeletableEntity
{
public string Name { get; set; }
}
var repository = new SoftDeleteMemoryRepository<Customer>();
// Soft delete - marks as deleted but doesn't remove from storage
await repository.Delete(customer);
// Normal queries automatically exclude soft-deleted entities
var activeCustomers = await repository.Get();
// Include soft-deleted entities
var allCustomers = await repository.GetIncludingDeleted();
// Restore a soft-deleted entity
await repository.Restore(customer);
// Permanently delete
await repository.PermanentlyDelete(customer);
Entity Framework Core Usage
using Microsoft.EntityFrameworkCore;
using OakIdeas.GenericRepository.EntityFrameworkCore;
public class MyDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
}
var context = new MyDbContext(options);
var repository = new EntityFrameworkCoreRepository<Customer, MyDbContext>(context);
// Use the same repository interface
var customers = await repository.Get(
filter: c => c.IsActive,
orderBy: q => q.OrderBy(c => c.Name),
includeProperties: "Orders,Address"
);
Batch Operations
// Insert multiple entities
var newCustomers = new List<Customer>
{
new() { Name = "Alice" },
new() { Name = "Bob" },
new() { Name = "Charlie" }
};
var inserted = await repository.InsertRange(newCustomers);
// Update multiple entities
var updated = await repository.UpdateRange(newCustomers);
// Delete multiple entities
var deletedCount = await repository.DeleteRange(newCustomers);
// Delete by filter
var deletedByFilter = await repository.DeleteRange(c => c.IsActive == false);
Async Enumerable for Large Datasets
// Stream results without loading everything into memory
await foreach (var customer in repository.GetAsyncEnumerable(
filter: c => c.CreatedDate > DateTime.UtcNow.AddYears(-1),
orderBy: q => q.OrderBy(c => c.Name)))
{
// Process each customer one at a time
await ProcessCustomerAsync(customer);
}
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
For detailed contribution guidelines, see docs/contributing.md.
Documentation
- Getting Started - Quick start guide
- Architecture - System architecture overview
- Best Practices - Recommended patterns and practices
- API Reference - Detailed API documentation
For AI Assistants & Developers
- AI Context - Overview for AI assistants working with this codebase
- Copilot Instructions - GitHub Copilot coding guidelines
- AGENTS.MD - Comprehensive agent capabilities and workflows
- GitHub Copilot Context - Quick reference for Copilot
License
This project is licensed under the terms specified in the LICENSE file.
| 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.Bcl.AsyncInterfaces (>= 10.0.1)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on OakIdeas.GenericRepository:
| Package | Downloads |
|---|---|
|
OakIdeas.GenericRepository.EntityFrameworkCore
A very simple extention to OakIdeas.GenericRepository that adds support for EF Core |
|
|
OakIdeas.GenericRepository.Middleware
Middleware infrastructure for OakIdeas.GenericRepository - enables extensible pipeline processing for repository operations. |
|
|
OakIdeas.GenericRepository.Memory
In-memory implementation of OakIdeas.GenericRepository - suitable for testing and development scenarios. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.7-alpha | 33 | 1/9/2026 |
| 0.0.6-alpha | 40 | 1/8/2026 |
| 0.0.5.1-alpha | 536 | 7/28/2020 |
| 0.0.5-alpha | 729 | 7/24/2020 |
- Generic interface
- EntityBase
- Memory Repository (Dictionary based)