BerrishDev.Common.Repository
8.0.1
dotnet add package BerrishDev.Common.Repository --version 8.0.1
NuGet\Install-Package BerrishDev.Common.Repository -Version 8.0.1
<PackageReference Include="BerrishDev.Common.Repository" Version="8.0.1" />
<PackageVersion Include="BerrishDev.Common.Repository" Version="8.0.1" />
<PackageReference Include="BerrishDev.Common.Repository" />
paket add BerrishDev.Common.Repository --version 8.0.1
#r "nuget: BerrishDev.Common.Repository, 8.0.1"
#:package BerrishDev.Common.Repository@8.0.1
#addin nuget:?package=BerrishDev.Common.Repository&version=8.0.1
#tool nuget:?package=BerrishDev.Common.Repository&version=8.0.1
Common.Repository
A powerful and flexible repository pattern implementation for Entity Framework Core that simplifies data access and provides a clean abstraction layer for your .NET applications.
🚀 Features
- Generic Repository Pattern: Type-safe repository implementations for any entity
- Query Repository: Optimized read-only operations with advanced querying capabilities
- Unit of Work Pattern: Transaction management with scoped operations
- Pagination Support: Built-in pagination with
PagedList<T>
andPagingDetails
- Sorting Support: Flexible sorting with
SortingDetails<T>
andSortItem
- Dependency Injection: Seamless integration with .NET DI container
- Save Change Strategies: Configurable save strategies (PerOperation/PerUnitOfWork)
- Async/Await Support: Full async support throughout the library
- Entity Framework Core Integration: Built specifically for EF Core 6.0+
📦 Installation
dotnet add package BerrishDev.Common.Repository
🏗️ Architecture
The library follows clean architecture principles and provides:
- Repository Interfaces:
IRepository<T>
andIQueryRepository<T>
- EF Core Implementation:
EFCoreRepository<TDbContext, TEntity>
andEfCoreQueryRepository<TDbContext, TEntity>
- Unit of Work:
IUnitOfWork
andIUnitOfWorkScope
for transaction management - Pagination:
PagedList<T>
andPagingDetails
for efficient data paging - Sorting:
SortingDetails<T>
,SortItem
, andSortDirection
for flexible data ordering
🚀 Quick Start
1. Configure Services
using Common.Repository.EfCore.Extensions;
using Common.Repository.EfCore.Options;
// In your Program.cs or Startup.cs
services.AddEfCoreDbContext<YourDbContext>(options =>
{
options.UseSqlServer(connectionString);
}, repositoryOptions: options =>
{
options.SaveChangeStrategy = SaveChangeStrategy.PerUnitOfWork;
});
services.AddUnitOfWork();
2. Create Your Entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public DateTime CreatedAt { get; set; }
}
3. Use the Repository
public class ProductService
{
private readonly IRepository<Product> _repository;
private readonly IQueryRepository<Product> _queryRepository;
private readonly IUnitOfWork _unitOfWork;
public ProductService(
IRepository<Product> repository,
IQueryRepository<Product> queryRepository,
IUnitOfWork unitOfWork)
{
_repository = repository;
_queryRepository = queryRepository;
_unitOfWork = unitOfWork;
}
public async Task<Product> CreateProductAsync(Product product, CancellationToken cancellationToken = default)
{
using var scope = await _unitOfWork.CreateScopeAsync(cancellationToken);
var createdProduct = await _repository.InsertAsync(product, cancellationToken);
await scope.CompletAsync(cancellationToken);
return createdProduct;
}
public async Task<PagedList<Product>> GetProductsAsync(
int pageIndex,
int pageSize,
CancellationToken cancellationToken = default)
{
return await _queryRepository.GetListByPageAsync(
pageIndex,
pageSize,
cancellationToken: cancellationToken);
}
}
📚 API Reference
Repository Interfaces
IRepository<TEntity>
Provides full CRUD operations:
public interface IRepository<TEntity> : IQueryRepository<TEntity>
{
// Insert operations
Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default);
// Update operations
Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);
// Delete operations
Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);
// Query operations with update tracking
Task<List<TEntity>> GetListForUpdateAsync(
List<Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>>>? relatedProperties = null,
Expression<Func<TEntity, bool>>? predicate = null,
SortingDetails<TEntity>? sortingDetails = null,
int? skip = null,
int? take = null,
CancellationToken cancellationToken = default);
Task<TEntity> GetForUpdateAsync(
Expression<Func<TEntity, bool>> predicate,
List<Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>>>? relatedProperties = null,
CancellationToken cancellationToken = default);
}
IQueryRepository<TEntity>
Provides read-only operations:
public interface IQueryRepository<TEntity>
{
// Basic query operations
Task<List<TEntity>> GetListAsync(
List<Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>>>? relatedProperties = null,
Expression<Func<TEntity, bool>>? predicate = null,
SortingDetails<TEntity>? sortingDetails = null,
int? skip = null,
int? take = null,
CancellationToken cancellationToken = default);
// Pagination
Task<PagedList<TEntity>> GetListByPageAsync(
int pageIndex,
int pageSize,
Expression<Func<TEntity, bool>>? predicate = null,
List<Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>>>? relatedProperties = null,
SortingDetails<TEntity>? sortingDetails = null,
CancellationToken cancellationToken = default);
// Single entity operations
Task<TEntity> GetAsync(
Expression<Func<TEntity, bool>> predicate,
List<Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>>>? relatedProperties = null,
CancellationToken cancellationToken = default);
// Aggregation operations
Task<long> CountAsync(
Expression<Func<TEntity, bool>>? predicate = null,
CancellationToken cancellationToken = default);
Task<bool> ExistsAsync(
Expression<Func<TEntity, bool>>? predicate = null,
CancellationToken cancellationToken = default);
}
Unit of Work
IUnitOfWork
public interface IUnitOfWork
{
Task<IUnitOfWorkScope> CreateScopeAsync(CancellationToken cancellationToken = default);
}
IUnitOfWorkScope
public interface IUnitOfWorkScope : IDisposable
{
Task CompletAsync(CancellationToken cancellationToken = default);
}
Pagination
PagedList<T>
public class PagedList<TItem>
{
public PagingDetails PagingDetails { get; }
public List<TItem> List { get; }
}
PagingDetails
public class PagingDetails
{
public int PageIndex { get; }
public int PageSize { get; }
public int TotalCount { get; }
public int TotalPages { get; }
public bool HasPreviousPage { get; }
public bool HasNextPage { get; }
}
Sorting
SortingDetails<T>
public class SortingDetails<TEntity>
{
public List<SortItem> SortItems { get; set; } = new();
}
SortItem
public class SortItem
{
public string PropertyName { get; set; }
public SortDirection Direction { get; set; }
}
SortDirection
public enum SortDirection
{
ASC,
DESC
}
🔧 Configuration Options
SaveChangeStrategy
Configure when changes are saved to the database:
public enum SaveChangeStrategy
{
PerOperation, // Save changes after each operation
PerUnitOfWork // Save changes only when unit of work completes
}
RepositoryAttribute
Control repository generation for specific DbSet properties:
public class YourDbContext : DbContext
{
[Repository(CreateGenericRepository = false, CreateQueryRepository = true)]
public DbSet<ReadOnlyEntity> ReadOnlyEntities { get; set; }
[Repository(CreateGenericRepository = true, CreateQueryRepository = false)]
public DbSet<WriteOnlyEntity> WriteOnlyEntities { get; set; }
}
📖 Examples
Advanced Querying with Includes
// Get products with categories and suppliers
var products = await _queryRepository.GetListAsync(
relatedProperties: new List<Func<IQueryable<Product>, IIncludableQueryable<Product, object>>>
{
q => q.Include(p => p.Category),
q => q.Include(p => p.Supplier)
},
predicate: p => p.Price > 100,
sortingDetails: new SortingDetails<Product>
{
SortItems = new List<SortItem>
{
new() { PropertyName = "Name", Direction = SortDirection.ASC },
new() { PropertyName = "Price", Direction = SortDirection.DESC }
}
},
skip: 10,
take: 20
);
Pagination with Filtering
// Get paginated products by category
var pagedProducts = await _queryRepository.GetListByPageAsync(
pageIndex: 0,
pageSize: 10,
predicate: p => p.CategoryId == categoryId,
sortingDetails: new SortingDetails<Product>
{
SortItems = new List<SortItem>
{
new() { PropertyName = "CreatedAt", Direction = SortDirection.DESC }
}
}
);
// Access pagination details
Console.WriteLine($"Total items: {pagedProducts.PagingDetails.TotalCount}");
Console.WriteLine($"Total pages: {pagedProducts.PagingDetails.TotalPages}");
Console.WriteLine($"Has next page: {pagedProducts.PagingDetails.HasNextPage}");
Transaction Management
public async Task TransferMoneyAsync(int fromAccountId, int toAccountId, decimal amount)
{
using var scope = await _unitOfWork.CreateScopeAsync();
try
{
var fromAccount = await _repository.GetForUpdateAsync(a => a.Id == fromAccountId);
var toAccount = await _repository.GetForUpdateAsync(a => a.Id == toAccountId);
fromAccount.Balance -= amount;
toAccount.Balance += amount;
await _repository.UpdateAsync(fromAccount);
await _repository.UpdateAsync(toAccount);
await scope.CompletAsync();
}
catch
{
// Transaction will be rolled back automatically
throw;
}
}
🏗️ Project Structure
Common.Repository/
├── Common.Repository/ # Main library
│ ├── EfCore/ # EF Core specific implementations
│ │ ├── Extensions/ # Service collection extensions
│ │ └── Options/ # Configuration options
│ ├── Repository/ # Repository interfaces and implementations
│ │ └── EfCore/ # EF Core repository implementations
│ ├── UnitOfWork/ # Unit of work interfaces and implementations
│ │ └── EfCore/ # EF Core unit of work implementations
│ ├── Lists/ # Pagination and sorting utilities
│ │ ├── Pagination/ # Paging classes
│ │ └── Sorting/ # Sorting classes
│ └── Exceptions/ # Custom exceptions
├── Sample/ # Example implementation
│ ├── Sample.API/ # Web API example
│ ├── Sample.Application/ # Application layer example
│ ├── Sample.Domain/ # Domain entities example
│ └── Sample.Persistence/ # Data access layer example
└── Common.Repository.sln # Solution file
🤝 Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👨💻 Author
Mikheil berishvili - GitHub
🙏 Acknowledgments
- Entity Framework Core team for the excellent ORM
- .NET community for inspiration and best practices
- All contributors who help improve this library
Note: This library is designed for .NET 6.0+ and Entity Framework Core 6.0+. Make sure your project targets the appropriate framework version.
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.18)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.2)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.