DKNet.EfCore.Repos.Abstractions 9.0.27

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

DKNet.EfCore.Repos.Abstractions

NuGet NuGet Downloads .NET License

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 operations
  • IReadRepository<TEntity> - Read-only operations and queries
  • IWriteRepository<TEntity> - Write operations and transaction management

Read Operations

  • Gets() - Get IQueryable for entity
  • GetDto<TModel>(Expression<Func<TEntity, bool>>?) - Get projection with optional filter
  • FindAsync(object, CancellationToken) - Find entity by primary key
  • FindAsync(object[], CancellationToken) - Find entity by composite key
  • AnyAsync(Expression<Func<TEntity, bool>>, CancellationToken) - Check if any entity matches condition
  • CountAsync(Expression<Func<TEntity, bool>>, CancellationToken) - Count entities matching condition

Write Operations

  • AddAsync(TEntity, CancellationToken) - Add single entity
  • AddRangeAsync(IEnumerable<TEntity>, CancellationToken) - Add multiple entities
  • UpdateAsync(TEntity, CancellationToken) - Update single entity
  • UpdateRangeAsync(IEnumerable<TEntity>, CancellationToken) - Update multiple entities
  • DeleteAsync(TEntity, CancellationToken) - Delete single entity
  • DeleteRangeAsync(IEnumerable<TEntity>, CancellationToken) - Delete multiple entities
  • SaveChangesAsync(CancellationToken) - Persist changes to database

Transaction Management

  • BeginTransactionAsync(CancellationToken) - Begin database transaction
  • Entry(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.


Part of the DKNet Framework - A comprehensive .NET framework for building modern, scalable applications.

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.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