DKNet.EfCore.Repos.Abstractions
9.0.27
See the version list below for details.
dotnet add package DKNet.EfCore.Repos.Abstractions --version 9.0.27
NuGet\Install-Package DKNet.EfCore.Repos.Abstractions -Version 9.0.27
<PackageReference Include="DKNet.EfCore.Repos.Abstractions" Version="9.0.27" />
<PackageVersion Include="DKNet.EfCore.Repos.Abstractions" Version="9.0.27" />
<PackageReference Include="DKNet.EfCore.Repos.Abstractions" />
paket add DKNet.EfCore.Repos.Abstractions --version 9.0.27
#r "nuget: DKNet.EfCore.Repos.Abstractions, 9.0.27"
#:package DKNet.EfCore.Repos.Abstractions@9.0.27
#addin nuget:?package=DKNet.EfCore.Repos.Abstractions&version=9.0.27
#tool nuget:?package=DKNet.EfCore.Repos.Abstractions&version=9.0.27
DKNet.EfCore.Repos.Abstractions
Repository pattern abstractions for Entity Framework Core, providing clean separation between read and write operations with strongly-typed interfaces. This package defines the contracts for data access operations following CQRS principles and Domain-Driven Design patterns.
Features
- Repository Pattern Abstractions: Clean interfaces for data access operations
- CQRS Support: Separate read and write operations with dedicated interfaces
- Generic Entity Support: Type-safe operations for any entity type
- Async/Await Operations: Full async support with cancellation tokens
- Transaction Management: Built-in transaction support for write operations
- Projection Support: Efficient DTO projections for read operations
- Query Flexibility: IQueryable support for complex queries
- Batch Operations: Support for bulk add, update, and delete operations
Supported Frameworks
- .NET 9.0+
- Entity Framework Core 9.0+
Installation
Install via NuGet Package Manager:
dotnet add package DKNet.EfCore.Repos.Abstractions
Or via Package Manager Console:
Install-Package DKNet.EfCore.Repos.Abstractions
Quick Start
Basic Repository Interface Usage
using DKNet.EfCore.Repos.Abstractions;
// Domain entity
public class Product : Entity<Guid>
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
// Custom repository interface
public interface IProductRepository : IRepository<Product>
{
Task<IEnumerable<Product>> GetProductsByCategoryAsync(string category, CancellationToken cancellationToken = default);
Task<bool> ExistsByNameAsync(string name, CancellationToken cancellationToken = default);
}
// Service using repository
public class ProductService
{
private readonly IProductRepository _productRepository;
public ProductService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public async Task<Product> CreateProductAsync(string name, decimal price, string category)
{
// Check if product already exists
if (await _productRepository.ExistsByNameAsync(name))
throw new InvalidOperationException($"Product with name '{name}' already exists");
var product = new Product
{
Name = name,
Price = price,
Category = category
};
await _productRepository.AddAsync(product);
await _productRepository.SaveChangesAsync();
return product;
}
}
Read-Only Operations
public class ProductQueryService
{
private readonly IReadRepository<Product> _readRepository;
public ProductQueryService(IReadRepository<Product> readRepository)
{
_readRepository = readRepository;
}
public async Task<List<ProductDto>> GetActiveProductsAsync()
{
// Use projection for efficient queries
return await _readRepository
.GetDto<ProductDto>(p => p.IsActive)
.OrderBy(p => p.Name)
.ToListAsync();
}
public async Task<Product?> GetProductByIdAsync(Guid id)
{
return await _readRepository.FindAsync(id);
}
public IQueryable<Product> GetProductsQuery()
{
// Return IQueryable for complex filtering
return _readRepository.Gets();
}
}
public class ProductDto
{
public Guid Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
Write Operations with Transactions
public class ProductManagementService
{
private readonly IWriteRepository<Product> _writeRepository;
public ProductManagementService(IWriteRepository<Product> writeRepository)
{
_writeRepository = writeRepository;
}
public async Task BulkUpdatePricesAsync(List<ProductPriceUpdate> updates)
{
using var transaction = await _writeRepository.BeginTransactionAsync();
try
{
foreach (var update in updates)
{
var product = await _writeRepository.FindAsync(update.ProductId);
if (product != null)
{
product.Price = update.NewPrice;
await _writeRepository.UpdateAsync(product);
}
}
await _writeRepository.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
public async Task AddProductsInBatchAsync(List<Product> products)
{
await _writeRepository.AddRangeAsync(products);
await _writeRepository.SaveChangesAsync();
}
}
public class ProductPriceUpdate
{
public Guid ProductId { get; set; }
public decimal NewPrice { get; set; }
}
Configuration
Repository Registration Pattern
using Microsoft.Extensions.DependencyInjection;
using DKNet.EfCore.Repos.Abstractions;
// Register repositories in DI container
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddRepositories(this IServiceCollection services)
{
// Register read-only repositories
services.AddScoped<IReadRepository<Product>, ProductReadRepository>();
services.AddScoped<IReadRepository<Customer>, CustomerReadRepository>();
// Register write repositories
services.AddScoped<IWriteRepository<Product>, ProductWriteRepository>();
services.AddScoped<IWriteRepository<Customer>, CustomerWriteRepository>();
// Register full repositories
services.AddScoped<IRepository<Product>, ProductRepository>();
services.AddScoped<IRepository<Customer>, CustomerRepository>();
// Register custom repositories
services.AddScoped<IProductRepository, ProductRepository>();
services.AddScoped<ICustomerRepository, CustomerRepository>();
return services;
}
}
API Reference
Core Interfaces
IRepository<TEntity>
- Combined read and write operationsIReadRepository<TEntity>
- Read-only operations and queriesIWriteRepository<TEntity>
- Write operations and transaction management
Read Operations
Gets()
- Get IQueryable for entityGetDto<TModel>(Expression<Func<TEntity, bool>>?)
- Get projection with optional filterFindAsync(object, CancellationToken)
- Find entity by primary keyFindAsync(object[], CancellationToken)
- Find entity by composite keyAnyAsync(Expression<Func<TEntity, bool>>, CancellationToken)
- Check if any entity matches conditionCountAsync(Expression<Func<TEntity, bool>>, CancellationToken)
- Count entities matching condition
Write Operations
AddAsync(TEntity, CancellationToken)
- Add single entityAddRangeAsync(IEnumerable<TEntity>, CancellationToken)
- Add multiple entitiesUpdateAsync(TEntity, CancellationToken)
- Update single entityUpdateRangeAsync(IEnumerable<TEntity>, CancellationToken)
- Update multiple entitiesDeleteAsync(TEntity, CancellationToken)
- Delete single entityDeleteRangeAsync(IEnumerable<TEntity>, CancellationToken)
- Delete multiple entitiesSaveChangesAsync(CancellationToken)
- Persist changes to database
Transaction Management
BeginTransactionAsync(CancellationToken)
- Begin database transactionEntry(TEntity)
- Get EntityEntry for change tracking
Advanced Usage
Custom Repository with Business Logic
public interface IOrderRepository : IRepository<Order>
{
Task<IEnumerable<Order>> GetOrdersByStatusAsync(OrderStatus status, CancellationToken cancellationToken = default);
Task<Order?> GetOrderWithItemsAsync(Guid orderId, CancellationToken cancellationToken = default);
Task<decimal> GetTotalSalesAsync(DateTime fromDate, DateTime toDate, CancellationToken cancellationToken = default);
}
public class OrderService
{
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
public OrderService(IOrderRepository orderRepository, IProductRepository productRepository)
{
_orderRepository = orderRepository;
_productRepository = productRepository;
}
public async Task<Order> CreateOrderAsync(Guid customerId, List<OrderItemRequest> items)
{
using var transaction = await _orderRepository.BeginTransactionAsync();
try
{
var order = new Order(customerId);
// Validate and add items
foreach (var item in items)
{
var product = await _productRepository.FindAsync(item.ProductId);
if (product == null)
throw new InvalidOperationException($"Product {item.ProductId} not found");
order.AddItem(item.ProductId, item.Quantity, product.Price);
}
await _orderRepository.AddAsync(order);
await _orderRepository.SaveChangesAsync();
await transaction.CommitAsync();
return order;
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
Repository with Specifications Pattern
public class ProductSpecificationService
{
private readonly IReadRepository<Product> _readRepository;
public ProductSpecificationService(IReadRepository<Product> readRepository)
{
_readRepository = readRepository;
}
public async Task<List<Product>> GetProductsAsync(ProductSearchCriteria criteria)
{
var query = _readRepository.Gets();
if (!string.IsNullOrEmpty(criteria.Category))
query = query.Where(p => p.Category == criteria.Category);
if (criteria.MinPrice.HasValue)
query = query.Where(p => p.Price >= criteria.MinPrice.Value);
if (criteria.MaxPrice.HasValue)
query = query.Where(p => p.Price <= criteria.MaxPrice.Value);
if (!string.IsNullOrEmpty(criteria.SearchTerm))
query = query.Where(p => p.Name.Contains(criteria.SearchTerm));
return await query
.OrderBy(p => p.Name)
.Skip(criteria.Skip)
.Take(criteria.Take)
.ToListAsync();
}
}
public class ProductSearchCriteria
{
public string? Category { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public string? SearchTerm { get; set; }
public int Skip { get; set; } = 0;
public int Take { get; set; } = 50;
}
Unit of Work Pattern Integration
public interface IUnitOfWork
{
IProductRepository Products { get; }
ICustomerRepository Customers { get; }
IOrderRepository Orders { get; }
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
Task<IDbContextTransaction> BeginTransactionAsync(CancellationToken cancellationToken = default);
}
public class BusinessService
{
private readonly IUnitOfWork _unitOfWork;
public BusinessService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task ProcessOrderAsync(OrderRequest request)
{
using var transaction = await _unitOfWork.BeginTransactionAsync();
try
{
// Create customer if not exists
var customer = await _unitOfWork.Customers.FindAsync(request.CustomerId);
if (customer == null)
{
customer = new Customer(request.CustomerEmail);
await _unitOfWork.Customers.AddAsync(customer);
}
// Create order
var order = new Order(customer.Id);
await _unitOfWork.Orders.AddAsync(order);
// Update product inventory
foreach (var item in request.Items)
{
var product = await _unitOfWork.Products.FindAsync(item.ProductId);
if (product != null)
{
product.ReduceInventory(item.Quantity);
await _unitOfWork.Products.UpdateAsync(product);
}
}
await _unitOfWork.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
}
}
Design Principles
CQRS Separation
- IReadRepository: Optimized for queries and projections
- IWriteRepository: Focused on data modifications and transactions
- IRepository: Combines both when full access is needed
Entity Framework Integration
- Direct integration with EF Core change tracking
- Transaction support through IDbContextTransaction
- IQueryable support for deferred execution
Testability
- Interface-based design for easy mocking
- Separation of concerns between read and write operations
- Clear contracts for business logic testing
Performance Considerations
- Projections: Use
GetDto<T>()
for efficient queries that only select needed columns - IQueryable: Leverage deferred execution for complex query building
- Batch Operations: Use range methods for bulk operations
- Transactions: Use transactions for atomic operations across multiple entities
- Change Tracking: EF Core change tracking is automatically managed
Thread Safety
- Repository instances should be scoped to request/operation lifetime
- Concurrent access to different entities is safe
- Shared entity instances require external synchronization
- Transaction isolation follows EF Core/database provider rules
Contributing
See the main CONTRIBUTING.md for guidelines on how to contribute to this project.
License
This project is licensed under the MIT License.
Related Packages
- DKNet.EfCore.Repos - Concrete repository implementations
- DKNet.EfCore.Abstractions - Core entity abstractions
- DKNet.EfCore.Extensions - EF Core functionality extensions
- DKNet.EfCore.Specifications - Specification pattern support
Part of the DKNet Framework - A comprehensive .NET framework for building modern, scalable applications.
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.Abstractions (>= 9.0.27)
- Microsoft.EntityFrameworkCore (>= 9.0.9)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DKNet.EfCore.Repos.Abstractions:
Package | Downloads |
---|---|
DKNet.EfCore.Repos
Package Description |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
9.0.28 | 25 | 9/17/2025 |
9.0.27 | 24 | 9/17/2025 |
9.0.26 | 39 | 9/16/2025 |
9.0.25 | 35 | 9/15/2025 |
9.0.24 | 34 | 9/15/2025 |
9.0.23 | 109 | 9/6/2025 |
9.0.22 | 163 | 9/3/2025 |
9.0.21 | 133 | 9/1/2025 |
9.0.20 | 151 | 7/15/2025 |
9.0.19 | 146 | 7/14/2025 |
9.0.18 | 148 | 7/14/2025 |
9.0.17 | 141 | 7/14/2025 |
9.0.16 | 129 | 7/11/2025 |
9.0.15 | 129 | 7/11/2025 |
9.0.14 | 129 | 7/11/2025 |
9.0.13 | 138 | 7/11/2025 |
9.0.12 | 157 | 7/8/2025 |
9.0.11 | 146 | 7/8/2025 |
9.0.10 | 150 | 7/7/2025 |
9.0.9 | 148 | 7/2/2025 |
9.0.8 | 148 | 7/2/2025 |
9.0.7 | 160 | 7/1/2025 |
9.0.6 | 153 | 6/30/2025 |
9.0.5 | 150 | 6/24/2025 |
9.0.4 | 148 | 6/24/2025 |
9.0.3 | 153 | 6/23/2025 |
9.0.2 | 154 | 6/23/2025 |
9.0.1 | 155 | 6/23/2025 |