Myth.Repository.EntityFramework
3.0.4
See the version list below for details.
dotnet add package Myth.Repository.EntityFramework --version 3.0.4
NuGet\Install-Package Myth.Repository.EntityFramework -Version 3.0.4
<PackageReference Include="Myth.Repository.EntityFramework" Version="3.0.4" />
<PackageVersion Include="Myth.Repository.EntityFramework" Version="3.0.4" />
<PackageReference Include="Myth.Repository.EntityFramework" />
paket add Myth.Repository.EntityFramework --version 3.0.4
#r "nuget: Myth.Repository.EntityFramework, 3.0.4"
#:package Myth.Repository.EntityFramework@3.0.4
#addin nuget:?package=Myth.Repository.EntityFramework&version=3.0.4
#tool nuget:?package=Myth.Repository.EntityFramework&version=3.0.4
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.
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<TEntity>: Read-only operationsWriteRepositoryAsync<TEntity>: Write operations (Add, Update, Remove)ReadWriteRepositoryAsync<TEntity>: 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<ApplicationDbContext> options ) : base( options ) { }
public DbSet<User> Users { get; set; }
public DbSet<Product> Products { get; set; }
}
The BaseContext automatically discovers and applies all IEntityTypeConfiguration<T> implementations in your assembly.
2. Create Repository Interface
using Myth.Interfaces.Repositories.EntityFramework;
public interface IUserRepository : IReadWriteRepositoryAsync<User> {
// Add custom methods if needed
Task<User?> GetByEmailAsync( string email, CancellationToken cancellationToken = default );
}
3. Implement Repository
using Myth.Repositories.EntityFramework;
public class UserRepository : ReadWriteRepositoryAsync<User>, IUserRepository {
public UserRepository( ApplicationDbContext context ) : base( context ) { }
public async Task<User?> 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<ApplicationDbContext>( 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<User?> 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<T> from the assembly
modelBuilder.ApplyConfigurationsFromAssembly( GetType( ).Assembly );
}
}
Benefits:
- No manual configuration registration
- Convention-based entity mapping discovery
- Cleaner, more maintainable code
📂 Repository Types
IReadRepositoryAsync<TEntity>
Read-only repository for query operations.
Core Methods:
// Basic queries
Task<IEnumerable<TEntity>> ToListAsync( CancellationToken cancellationToken = default );
IQueryable<TEntity> AsQueryable( );
IEnumerable<TEntity> AsEnumerable( );
// Filtered queries
IQueryable<TEntity> Where( Expression<Func<TEntity, bool>> predicate );
Task<IEnumerable<TEntity>> SearchAsync( Expression<Func<TEntity, bool>> filter, Expression<Func<TEntity, bool>>? orderBy = null, CancellationToken cancellationToken = default );
// Pagination
Task<IPaginated<TEntity>> SearchPaginatedAsync( Expression<Func<TEntity, bool>> predicate, int take = 0, int skip = 0, Expression<Func<TEntity, bool>>? orderBy = null, CancellationToken cancellationToken = default );
Task<IPaginated<TEntity>> SearchPaginatedAsync( Expression<Func<TEntity, bool>> predicate, Pagination pagination, Expression<Func<TEntity, bool>>? orderBy = null, CancellationToken cancellationToken = default );
// Single item retrieval
Task<TEntity?> FirstOrDefaultAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
Task<TEntity?> LastOrDefaultAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
Task<TEntity> FirstAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
Task<TEntity> LastAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
// Aggregate operations
Task<int> CountAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
Task<bool> AnyAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
Task<bool> AllAsync( Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default );
// Metadata
string? GetProviderName( );
Specification Pattern Support:
All methods have specification-based overloads:
Task<IEnumerable<TEntity>> SearchAsync( ISpec<TEntity> specification, CancellationToken cancellationToken = default );
Task<IPaginated<TEntity>> SearchPaginatedAsync( ISpec<TEntity> specification, CancellationToken cancellationToken = default );
IQueryable<TEntity> Where( ISpec<TEntity> specification );
Task<int> CountAsync( ISpec<TEntity> specification, CancellationToken cancellationToken = default );
// ... and more
IWriteRepositoryAsync<TEntity>
Write operations for creating, updating, and deleting entities.
Methods:
// Create
Task AddAsync( TEntity entity, CancellationToken cancellationToken = default );
Task AddRangeAsync( IEnumerable<TEntity> entities, CancellationToken cancellationToken = default );
// Update
Task UpdateAsync( TEntity entity, CancellationToken cancellationToken = default );
Task UpdateRangeAsync( IEnumerable<TEntity> entities, CancellationToken cancellationToken = default );
// Delete
Task RemoveAsync( TEntity entity, CancellationToken cancellationToken = default );
Task RemoveRangeAsync( IEnumerable<TEntity> entities, CancellationToken cancellationToken = default );
// Entity state management
Task AttachAsync( TEntity entity, CancellationToken cancellationToken = default );
Task AttachRangeAsync( IEnumerable<TEntity> entities, CancellationToken cancellationToken = default );
IReadWriteRepositoryAsync<TEntity>
Combines both read and write operations. This is the most commonly used repository type.
Usage:
public class ProductRepository : ReadWriteRepositoryAsync<Product>, 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<User> IsActive( this ISpec<User> spec ) {
return spec.And( u => u.IsActive );
}
public static ISpec<User> HasRole( this ISpec<User> spec, string role ) {
return spec.And( u => u.Role == role );
}
}
// Use in repository
public class UserRepository : ReadWriteRepositoryAsync<User>, IUserRepository {
public UserRepository( ApplicationDbContext context ) : base( context ) { }
public async Task<IEnumerable<User>> GetActiveAdminsAsync( ) {
var spec = SpecBuilder<User>.Create( )
.IsActive( )
.HasRole( "Admin" )
.Order( u => u.Name );
return await SearchAsync( spec );
}
public async Task<IPaginated<User>> GetActiveUsersPaginatedAsync( int page, int pageSize ) {
var spec = SpecBuilder<User>.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<IPaginated<Product>> 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<IPaginated<Product>> GetProductsAsync( Pagination pagination ) {
return await _productRepository.SearchPaginatedAsync(
predicate: p => p.IsAvailable,
pagination: pagination,
orderPredicate: p => p.Name
);
}
Result Structure:
public interface IPaginated<T> {
IEnumerable<T> 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<Order> {
Task<IEnumerable<Order>> GetOrdersByCustomerAsync( Guid customerId, CancellationToken cancellationToken = default );
Task<Order?> GetOrderWithItemsAsync( Guid orderId, CancellationToken cancellationToken = default );
Task<decimal> GetTotalRevenueAsync( DateTime from, DateTime to, CancellationToken cancellationToken = default );
}
public class OrderRepository : ReadWriteRepositoryAsync<Order>, IOrderRepository {
public OrderRepository( ApplicationDbContext context ) : base( context ) { }
public async Task<IEnumerable<Order>> GetOrdersByCustomerAsync( Guid customerId, CancellationToken cancellationToken = default ) {
return await SearchAsync( o => o.CustomerId == customerId, o => o.OrderDate, cancellationToken );
}
public async Task<Order?> 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<decimal> 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<int> SaveChangesAsync( CancellationToken cancellationToken = default );
Task<int> ExecuteSqlAsync( string query, IEnumerable<object>? parameters = null, CancellationToken cancellationToken = default );
}
Implementation
// Create custom Unit of Work
public class ApplicationUnitOfWork : BaseUnitOfWorkRepository {
public ApplicationUnitOfWork( ApplicationDbContext context ) : base( context ) { }
}
// Or use generic implementation
public class GenericUnitOfWork : UnitOfWorkRepository<ApplicationDbContext> {
public GenericUnitOfWork( ApplicationDbContext context ) : base( context ) { }
}
Registration
// Option 1: Custom implementation
builder.Services.AddUnitOfWork<ApplicationUnitOfWork>( );
// Option 2: Generic implementation
builder.Services.AddUnitOfWorkForContext<ApplicationDbContext>( );
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<Result> 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<Result> 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<IUserRepository, UserRepository>( );
// With custom lifetime
builder.Services.AddRepository<IUserRepository, UserRepository>( ServiceLifetime.Transient );
Unit of Work Registration
// Register custom Unit of Work implementation
builder.Services.AddUnitOfWork<ApplicationUnitOfWork>( );
// Or register generic Unit of Work for specific context
builder.Services.AddUnitOfWorkForContext<ApplicationDbContext>( );
Complete Setup Example
var builder = WebApplication.CreateBuilder( args );
// Database configuration
builder.Services.AddDbContext<ApplicationDbContext>( 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<ApplicationDbContext>( );
// 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 | 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. net9.0 was computed. 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. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.6)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.6)
- Myth.DependencyInjection (>= 3.0.4)
- Myth.Repository (>= 3.0.4)
- Myth.Specification (>= 3.0.4)
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 |
|---|---|---|
| 3.0.5-preview.3 | 32 | 11/4/2025 |
| 3.0.5-preview.2 | 42 | 11/4/2025 |
| 3.0.5-preview.1 | 34 | 11/4/2025 |
| 3.0.4 | 87 | 11/3/2025 |
| 3.0.4-preview.19 | 48 | 11/2/2025 |
| 3.0.4-preview.17 | 46 | 11/1/2025 |
| 3.0.4-preview.16 | 49 | 11/1/2025 |
| 3.0.4-preview.15 | 56 | 10/31/2025 |
| 3.0.4-preview.14 | 126 | 10/31/2025 |
| 3.0.4-preview.13 | 125 | 10/30/2025 |
| 3.0.4-preview.12 | 118 | 10/23/2025 |
| 3.0.4-preview.11 | 115 | 10/23/2025 |
| 3.0.4-preview.10 | 112 | 10/23/2025 |
| 3.0.4-preview.9 | 112 | 10/23/2025 |
| 3.0.4-preview.8 | 118 | 10/22/2025 |
| 3.0.4-preview.6 | 111 | 10/21/2025 |
| 3.0.4-preview.5 | 111 | 10/21/2025 |
| 3.0.4-preview.4 | 114 | 10/20/2025 |
| 3.0.4-preview.3 | 114 | 10/20/2025 |
| 3.0.4-preview.2 | 36 | 10/18/2025 |
| 3.0.4-preview.1 | 119 | 10/7/2025 |
| 3.0.3 | 210 | 8/30/2025 |
| 3.0.2 | 107 | 8/23/2025 |
| 3.0.2-preview.4 | 128 | 8/21/2025 |
| 3.0.2-preview.3 | 101 | 8/16/2025 |
| 3.0.2-preview.1 | 80 | 5/23/2025 |
| 3.0.1 | 3,340 | 3/12/2025 |
| 3.0.1-preview.2 | 150 | 3/11/2025 |
| 3.0.1-preview.1 | 86 | 2/5/2025 |
| 3.0.0.2-preview | 122 | 12/10/2024 |
| 3.0.0.1-preview | 145 | 6/29/2024 |
| 3.0.0 | 4,686 | 12/10/2024 |
| 3.0.0-preview | 148 | 6/28/2024 |
| 2.0.0.17 | 1,981 | 4/17/2024 |
| 2.0.0.16 | 3,421 | 12/15/2023 |
| 2.0.0.15 | 209 | 12/15/2023 |
| 2.0.0.11 | 3,802 | 8/11/2022 |
| 2.0.0.10 | 2,687 | 7/20/2022 |
| 2.0.0.9 | 2,762 | 7/15/2022 |
| 2.0.0.8 | 2,793 | 7/12/2022 |
| 2.0.0.7 | 2,719 | 6/20/2022 |
| 2.0.0.6 | 2,801 | 5/23/2022 |
| 2.0.0.5 | 2,761 | 5/18/2022 |
| 2.0.0 | 2,873 | 2/17/2022 |