Myth.Repository.EntityFramework
4.4.3
dotnet add package Myth.Repository.EntityFramework --version 4.4.3
NuGet\Install-Package Myth.Repository.EntityFramework -Version 4.4.3
paket add Myth.Repository.EntityFramework --version 4.4.3
#r "nuget: Myth.Repository.EntityFramework, 4.4.3"
#:package Myth.Repository.EntityFramework@4.4.3
#addin nuget:?package=Myth.Repository.EntityFramework&version=4.4.3
#tool nuget:?package=Myth.Repository.EntityFramework&version=4.4.3

Myth.Repository.EntityFramework
A comprehensive .NET library that provides robust, feature-rich repository implementations for Entity Framework Core with Domain-Driven Design (DDD) patterns, Specification pattern support, and automatic dependency injection.
🎯 Why Myth.Repository.EntityFramework?
Implementing Repository pattern with EF Core is repetitive boilerplate. Every project rewrites GenericRepository, copy-pastes DbContext configuration, manually wires up UnitOfWork. Myth.Repository.EntityFramework provides battle-tested implementations eliminating 90% of repository boilerplate while adding Specification pattern support, auto-discovery of entity configurations, and proper Unit of Work.
The Problem
Manual GenericRepository implementation in every project. DbContext configuration scattered. No Specification integration. Unit of Work manually managed. Testing requires complex setup.
The Solution
Ready-to-use repositories: ReadRepositoryAsync, WriteRepositoryAsync, ReadWriteRepositoryAsync. BaseContext: Auto-discovers entity configurations via reflection. Specification integration: Pass ISpec directly to repository methods. Unit of Work: Built-in IUnitOfWork for transaction management. Auto-registration: DI extension methods register all repositories.
Key Benefits
90% less code: No need to implement repositories manually. Specification-powered: Complex queries via reusable specifications. DDD-ready: Read/write segregation, aggregate roots, Unit of Work pattern. Testable: Easy to mock, in-memory database support.
Table of Contents
- Features
- Installation
- Quick Start
- Core Concepts
- Repository Types
- Advanced Usage
- Unit of Work Pattern
- Dependency Injection
⭐ Features
- BaseContext: Abstract DbContext with automatic entity configuration discovery
- Repository Implementations:
ReadRepositoryAsync: Read-only operationsWriteRepositoryAsync: Write operations (Add, Update, Remove)ReadWriteRepositoryAsync: Combined read/write operations
- Specification Pattern Support: Seamlessly integrate with Myth.Specification
- Expression-Based Queries: Direct LINQ expression support
- Unit of Work Pattern: Transaction management with savepoints
- Automatic Dependency Injection: Zero-configuration repository registration
- Pagination Support: Built-in paginated query results
- Async/Await: Fully asynchronous API
- Entity Attach/Detach: Fine-grained entity state management
📦 Installation
Install via NuGet Package Manager:
dotnet add package Myth.Repository.EntityFramework
Or via Package Manager Console:
Install-Package Myth.Repository.EntityFramework
🚀 Quick Start
1. Create Your DbContext
using Myth.Contexts;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : BaseContext {
public ApplicationDbContext( DbContextOptions options ) : base( options ) { }
public DbSet Users { get; set; }
public DbSet Products { get; set; }
}
The BaseContext automatically discovers and applies all IEntityTypeConfiguration implementations in your assembly.
2. Create Repository Interface
using Myth.Interfaces.Repositories.EntityFramework;
public interface IUserRepository : IReadWriteRepositoryAsync {
// Add custom methods if needed
Task GetByEmailAsync( string email, CancellationToken cancellationToken = default );
}
3. Implement Repository
using Myth.Repositories.EntityFramework;
public class UserRepository : ReadWriteRepositoryAsync, IUserRepository {
public UserRepository( ApplicationDbContext context ) : base( context ) { }
public async Task GetByEmailAsync( string email, CancellationToken cancellationToken = default ) {
return await FirstOrDefaultAsync( u => u.Email == email, cancellationToken );
}
}
4. Register in Startup
var builder = WebApplication.CreateBuilder( args );
// Register DbContext
builder.Services.AddDbContext( options =>
options.UseSqlServer( builder.Configuration.GetConnectionString( "DefaultConnection" ) ) );
// Automatically register ALL repositories
builder.Services.AddRepositories( );
var app = builder.BuildApp( ); // Use BuildApp() for cross-library dependency resolution
app.Run( );
5. Use in Your Services
public class UserService {
private readonly IUserRepository _userRepository;
public UserService( IUserRepository userRepository ) {
_userRepository = userRepository;
}
public async Task GetUserByEmailAsync( string email ) {
return await _userRepository.GetByEmailAsync( email );
}
public async Task CreateUserAsync( User user ) {
await _userRepository.AddAsync( user );
await _userRepository.SaveChangesAsync( );
}
}
🎯 Core Concepts
BaseContext
The BaseContext is an abstract base class that extends DbContext and provides automatic entity configuration:
public abstract class BaseContext : DbContext {
protected override void OnModelCreating( ModelBuilder modelBuilder ) {
// Automatically applies all IEntityTypeConfiguration from the assembly
modelBuilder.ApplyConfigurationsFromAssembly( GetType( ).Assembly );
}
}
Benefits:
- No manual configuration registration
- Convention-based entity mapping discovery
- Cleaner, more maintainable code
📂 Repository Types
IReadRepositoryAsync
Read-only repository for query operations.
Core Methods:
// Basic queries
Task> ToListAsync( CancellationToken cancellationToken = default );
IQueryable AsQueryable( );
IEnumerable AsEnumerable( );
// Filtered queries — returns change-tracked entities (modifications saved via SaveChangesAsync)
IQueryable Where( Expression> predicate );
Task> SearchAsync( Expression> filter, Expression>? orderBy = null, CancellationToken cancellationToken = default );
// Read-only variant — no change tracking overhead, use for projections and reports
Task> SearchAsNoTrackingAsync( Expression> filter, Expression>? orderBy = null, CancellationToken cancellationToken = default );
// Pagination
Task> SearchPaginatedAsync( Expression> predicate, int take = 0, int skip = 0, Expression>? orderBy = null, CancellationToken cancellationToken = default );
Task> SearchPaginatedAsync( Expression> predicate, Pagination pagination, Expression>? orderBy = null, CancellationToken cancellationToken = default );
// Single item retrieval
Task FirstOrDefaultAsync( Expression> predicate, CancellationToken cancellationToken = default );
Task LastOrDefaultAsync( Expression> predicate, CancellationToken cancellationToken = default );
Task FirstAsync( Expression> predicate, CancellationToken cancellationToken = default );
Task LastAsync( Expression> predicate, CancellationToken cancellationToken = default );
// Aggregate operations
Task CountAsync( Expression> predicate, CancellationToken cancellationToken = default );
Task AnyAsync( Expression> predicate, CancellationToken cancellationToken = default );
Task AllAsync( Expression> predicate, CancellationToken cancellationToken = default );
// Metadata
string? GetProviderName( );
Specification Pattern Support:
All methods have specification-based overloads:
Task> SearchAsync( ISpec specification, CancellationToken cancellationToken = default );
Task> SearchAsNoTrackingAsync( ISpec specification, CancellationToken cancellationToken = default );
Task> SearchPaginatedAsync( ISpec specification, CancellationToken cancellationToken = default );
IQueryable Where( ISpec specification );
Task CountAsync( ISpec specification, CancellationToken cancellationToken = default );
// ... and more
IWriteRepositoryAsync
Write operations for creating, updating, and deleting entities.
Methods:
// Create
Task AddAsync( TEntity entity, CancellationToken cancellationToken = default );
Task AddRangeAsync( IEnumerable entities, CancellationToken cancellationToken = default );
// Update
Task UpdateAsync( TEntity entity, CancellationToken cancellationToken = default );
Task UpdateRangeAsync( IEnumerable entities, CancellationToken cancellationToken = default );
// Delete
Task RemoveAsync( TEntity entity, CancellationToken cancellationToken = default );
Task RemoveRangeAsync( IEnumerable entities, CancellationToken cancellationToken = default );
// Entity state management
Task AttachAsync( TEntity entity, CancellationToken cancellationToken = default );
Task AttachRangeAsync( IEnumerable entities, CancellationToken cancellationToken = default );
IReadWriteRepositoryAsync
Combines both read and write operations. This is the most commonly used repository type.
Usage:
public class ProductRepository : ReadWriteRepositoryAsync, IProductRepository {
public ProductRepository( ApplicationDbContext context ) : base( context ) { }
}
🔧 Advanced Usage
Working with Specifications
The library integrates seamlessly with Myth.Specification:
using Myth.Specification;
// Define specifications
public static class UserSpecifications {
public static ISpec IsActive( this ISpec spec ) {
return spec.And( u => u.IsActive );
}
public static ISpec HasRole( this ISpec spec, string role ) {
return spec.And( u => u.Role == role );
}
}
// Use in repository
public class UserRepository : ReadWriteRepositoryAsync, IUserRepository {
public UserRepository( ApplicationDbContext context ) : base( context ) { }
public async Task> GetActiveAdminsAsync( ) {
var spec = SpecBuilder.Create( )
.IsActive( )
.HasRole( "Admin" )
.Order( u => u.Name );
return await SearchAsync( spec );
}
public async Task> GetActiveUsersPaginatedAsync( int page, int pageSize ) {
var spec = SpecBuilder.Create( )
.IsActive( )
.Order( u => u.CreatedAt )
.Skip( ( page - 1 ) * pageSize )
.Take( pageSize );
return await SearchPaginatedAsync( spec );
}
}
Pagination Examples
Using Expression-Based Pagination:
public async Task> GetProductsAsync( int page, int pageSize ) {
var skip = ( page - 1 ) * pageSize;
return await _productRepository.SearchPaginatedAsync(
predicate: p => p.IsAvailable,
take: pageSize,
skip: skip,
orderPredicate: p => p.Price
);
}
Using Pagination Object:
public async Task> GetProductsAsync( Pagination pagination ) {
return await _productRepository.SearchPaginatedAsync(
predicate: p => p.IsAvailable,
pagination: pagination,
orderPredicate: p => p.Name
);
}
Result Structure:
public interface IPaginated {
IEnumerable Items { get; }
int TotalItems { get; }
int PageSize { get; }
int CurrentPage { get; }
int TotalPages { get; }
bool HasPrevious { get; }
bool HasNext { get; }
}
Custom Repository Methods
Extend base repositories with domain-specific methods:
public interface IOrderRepository : IReadWriteRepositoryAsync {
Task> GetOrdersByCustomerAsync( Guid customerId, CancellationToken cancellationToken = default );
Task GetOrderWithItemsAsync( Guid orderId, CancellationToken cancellationToken = default );
Task GetTotalRevenueAsync( DateTime from, DateTime to, CancellationToken cancellationToken = default );
}
public class OrderRepository : ReadWriteRepositoryAsync, IOrderRepository {
public OrderRepository( ApplicationDbContext context ) : base( context ) { }
public async Task> GetOrdersByCustomerAsync( Guid customerId, CancellationToken cancellationToken = default ) {
return await SearchAsync( o => o.CustomerId == customerId, o => o.OrderDate, cancellationToken );
}
public async Task GetOrderWithItemsAsync( Guid orderId, CancellationToken cancellationToken = default ) {
return await AsQueryable( )
.Include( o => o.OrderItems )
.ThenInclude( oi => oi.Product )
.FirstOrDefaultAsync( o => o.Id == orderId, cancellationToken );
}
public async Task GetTotalRevenueAsync( DateTime from, DateTime to, CancellationToken cancellationToken = default ) {
var orders = await SearchAsync( o => o.OrderDate >= from && o.OrderDate <= to, null, cancellationToken );
return orders.Sum( o => o.TotalAmount );
}
}
🪄 Unit of Work Pattern
The IUnitOfWorkRepository provides transaction management capabilities.
Interface
public interface IUnitOfWorkRepository : IAsyncDisposable {
Task BeginTransactionAsync( CancellationToken cancellationToken = default );
Task CommitAsync( CancellationToken cancellationToken = default );
Task RollbackAsync( CancellationToken cancellationToken = default );
Task CreateSavepointAsync( string savepointName, CancellationToken cancellationToken = default );
Task RollbackToSavepointAsync( string savepointName, CancellationToken cancellationToken = default );
Task SaveChangesAsync( CancellationToken cancellationToken = default );
Task ExecuteSqlAsync( string query, IEnumerable
Implementation
// Create custom Unit of Work
public class ApplicationUnitOfWork : BaseUnitOfWorkRepository {
public ApplicationUnitOfWork( ApplicationDbContext context ) : base( context ) { }
}
// Or use generic implementation
public class GenericUnitOfWork : UnitOfWorkRepository {
public GenericUnitOfWork( ApplicationDbContext context ) : base( context ) { }
}
Registration
// Option 1: Custom implementation
builder.Services.AddUnitOfWork( );
// Option 2: Generic implementation
builder.Services.AddUnitOfWorkForContext( );
Usage Example
Basic Transaction:
public class OrderService {
private readonly IOrderRepository _orderRepository;
private readonly IInventoryRepository _inventoryRepository;
private readonly IUnitOfWorkRepository _unitOfWork;
public OrderService( IOrderRepository orderRepository, IInventoryRepository inventoryRepository, IUnitOfWorkRepository unitOfWork ) {
_orderRepository = orderRepository;
_inventoryRepository = inventoryRepository;
_unitOfWork = unitOfWork;
}
public async Task CreateOrderAsync( CreateOrderRequest request ) {
await _unitOfWork.BeginTransactionAsync( );
try {
// Create order
var order = new Order { CustomerId = request.CustomerId, OrderDate = DateTime.UtcNow };
await _orderRepository.AddAsync( order );
await _unitOfWork.SaveChangesAsync( );
// Update inventory
foreach ( var item in request.Items ) {
var inventory = await _inventoryRepository.FirstOrDefaultAsync( i => i.ProductId == item.ProductId );
if ( inventory == null || inventory.Quantity < item.Quantity )
throw new InvalidOperationException( "Insufficient inventory" );
inventory.Quantity -= item.Quantity;
await _inventoryRepository.UpdateAsync( inventory );
}
await _unitOfWork.SaveChangesAsync( );
// Commit transaction
await _unitOfWork.CommitAsync( );
return Result.Success( );
}
catch ( Exception ex ) {
await _unitOfWork.RollbackAsync( );
return Result.Failure( ex.Message );
}
}
}
Transaction with Savepoints:
public async Task ComplexOperationAsync( ) {
await _unitOfWork.BeginTransactionAsync( );
try {
// Step 1: Create user
await CreateUserAsync( );
await _unitOfWork.SaveChangesAsync( );
await _unitOfWork.CreateSavepointAsync( "AfterUserCreation" );
// Step 2: Create profile (might fail)
try {
await CreateUserProfileAsync( );
await _unitOfWork.SaveChangesAsync( );
}
catch {
// Rollback to savepoint, keeping user creation
await _unitOfWork.RollbackToSavepointAsync( "AfterUserCreation" );
await CreateDefaultProfileAsync( );
await _unitOfWork.SaveChangesAsync( );
}
await _unitOfWork.CommitAsync( );
return Result.Success( );
}
catch ( Exception ex ) {
await _unitOfWork.RollbackAsync( );
return Result.Failure( ex.Message );
}
}
🚀 Dependency Injection
Automatic Registration
The library provides intelligent automatic registration:
// Registers ALL repository implementations from application assemblies
builder.Services.AddRepositories( );
Registration Rules:
- Scoped lifetime by default (recommended for repositories)
- Automatically excludes test types (Test, Mock, Fake, Stub)
- Automatically excludes generic type definitions
- Matches repositories to interfaces by naming convention
Registration from Specific Assembly
// Register repositories from a specific assembly
builder.Services.AddRepositoriesFromAssembly( typeof( UserRepository ).Assembly );
// With custom lifetime
builder.Services.AddRepositoriesFromAssembly( typeof( UserRepository ).Assembly, ServiceLifetime.Transient );
Manual Registration
// Register specific repository manually
builder.Services.AddRepository( );
// With custom lifetime
builder.Services.AddRepository( ServiceLifetime.Transient );
Unit of Work Registration
// Register custom Unit of Work implementation
builder.Services.AddUnitOfWork( );
// Or register generic Unit of Work for specific context
builder.Services.AddUnitOfWorkForContext( );
Complete Setup Example
var builder = WebApplication.CreateBuilder( args );
// Database configuration
builder.Services.AddDbContext( options => {
options.UseSqlServer( builder.Configuration.GetConnectionString( "DefaultConnection" ) );
options.EnableSensitiveDataLogging( builder.Environment.IsDevelopment( ) );
options.EnableDetailedErrors( builder.Environment.IsDevelopment( ) );
} );
// Repository registration
builder.Services.AddRepositories( ); // Auto-register all repositories
// Unit of Work registration
builder.Services.AddUnitOfWorkForContext( );
// Build app with cross-library dependency resolution
var app = builder.BuildApp( );
app.Run( );
📝 Best Practices
- Use Repository Interfaces: Always depend on interfaces, not concrete implementations
- Keep Repositories Thin: Complex business logic belongs in services, not repositories
- Leverage Specifications: Use Myth.Specification for reusable query logic
- Use Unit of Work for Transactions: When operations span multiple repositories
- Async All The Way: Always use async/await for database operations
- Dispose Properly: Repositories implement
IAsyncDisposablewhen needed - Use BuildApp(): Replace
builder.Build()withbuilder.BuildApp()for cross-library integration
🔗 Related Libraries
- Myth.Repository: Base repository interfaces and abstractions
- Myth.Specification: Specification pattern implementation for query building
- Myth.Commons: Common utilities and extensions
📄 License
Licensed under the Apache 2.0 License. See LICENSE for details.
| 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.EntityFrameworkCore (>= 10.0.8)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.8)
- Myth.DependencyInjection (>= 4.4.3)
- Myth.Repository (>= 4.4.3)
- Myth.Specification (>= 4.4.3)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Myth.Repository.EntityFramework:
| Package | Downloads |
|---|---|
|
Harpy.Domain
Basis for the domain layer of the Harpy Framework |
|
|
Harpy.Context
Basis for the context layer of the Harpy Framework |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 4.4.3 | 280 | 6/9/2026 |
| 4.4.2 | 149 | 6/7/2026 |
| 4.4.1 | 140 | 6/5/2026 |
| 4.4.0 | 146 | 6/5/2026 |
| 4.4.0-preview.12 | 82 | 6/4/2026 |
| 4.4.0-preview.11 | 90 | 6/4/2026 |
| 4.4.0-preview.10 | 87 | 6/2/2026 |
| 4.4.0-preview.9 | 75 | 6/2/2026 |
| 4.4.0-preview.8 | 121 | 3/12/2026 |
| 4.4.0-preview.7 | 85 | 3/12/2026 |
| 4.4.0-preview.6 | 112 | 2/20/2026 |
| 4.4.0-preview.5 | 102 | 2/19/2026 |
| 4.4.0-preview.4 | 84 | 2/18/2026 |
| 4.4.0-preview.3 | 86 | 2/18/2026 |
| 4.4.0-preview.2 | 105 | 2/17/2026 |
| 4.4.0-preview.1 | 101 | 2/14/2026 |
| 4.3.0 | 210 | 2/1/2026 |
| 4.3.0-preview.3 | 99 | 2/1/2026 |
| 4.3.0-preview.2 | 184 | 12/22/2025 |
| 4.2.1-preview.1 | 683 | 12/2/2025 |