EFGenericUnitOfWork 1.0.42
See the version list below for details.
dotnet add package EFGenericUnitOfWork --version 1.0.42
NuGet\Install-Package EFGenericUnitOfWork -Version 1.0.42
<PackageReference Include="EFGenericUnitOfWork" Version="1.0.42" />
<PackageVersion Include="EFGenericUnitOfWork" Version="1.0.42" />
<PackageReference Include="EFGenericUnitOfWork" />
paket add EFGenericUnitOfWork --version 1.0.42
#r "nuget: EFGenericUnitOfWork, 1.0.42"
#:package EFGenericUnitOfWork@1.0.42
#addin nuget:?package=EFGenericUnitOfWork&version=1.0.42
#tool nuget:?package=EFGenericUnitOfWork&version=1.0.42
EFGenericUnitOfWork
Author / Yazar: Mehmet SEYİTOĞLU
Company / Şirket: EMF BİLGİSAYAR YAZILIM YÖNETİM VE DANIŞMANLIK HİZMETLERİ
Target Framework: .NET 10
License / Lisans: MIT
🇹🇷 Türkçe Dokümantasyon
Genel Bakış
EFGenericUnitOfWork, Entity Framework Core üzerine inşa edilmiş, Repository Pattern, Unit of Work Pattern, SoftDelete ve Pagination desteği sunan genel amaçlı bir veri erişim kütüphanesidir.
Ne İşe Yarar?
- ✅ Veritabanı işlemlerini soyutlayarak temiz mimari sağlar
- ✅ Generic Repository ile CRUD işlemlerini tek merkezden yönetir
- ✅ SoftDelete desteği ile kayıtları fiziksel silmeden işaretler
- ✅ Sayfalama (Pagination) desteği sunar
- ✅ Transaction yönetimi sağlar
- ✅ SQL Function ve Stored Procedure desteği içerir
- ✅ Bulk (toplu) işlemler için optimize metotlar sunar
- ✅ Birden fazla veritabanı ile çalışma desteği (
RepositoryOutDbName) - ✅ Hem senkron hem asenkron metotlar içerir
Kurulum
dotnet add package EFGenericUnitOfWork
DI Kaydı (Dependency Injection)
// Program.cs veya Startup.cs
// 1. Standart kullanım (tek veritabanı)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IUnitOfWork>(provider =>
new UnitOfWork<AppDbContext>(provider.GetRequiredService<AppDbContext>()));
// 2. Dinamik veritabanı kullanımı (birden fazla veritabanı)
builder.Services.AddScoped<IDbContextFactory<AppDbContext>, AppDbContextFactory>();
builder.Services.AddScoped<IUnitOfWork>(provider =>
new UnitOfWork<AppDbContext>(provider.GetRequiredService<IDbContextFactory<AppDbContext>>()));
Temel Kullanım Örnekleri
Repository ile CRUD İşlemleri
public class ProductService
{
private readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// Tüm ürünleri getir
public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetAllAsync();
}
// Filtreleme, sıralama ve include ile getir
public async Task<IEnumerable<Product>> GetActiveProductsAsync()
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetAllAsync(
predicate: p => p.Price > 100,
orderBy: q => q.OrderByDescending(p => p.CreatedDate),
include: q => q.Include(p => p.Category),
asNoTracking: true
);
}
// ID ile getir
public async Task<Product> GetProductByIdAsync(int id)
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetByIdAsync(id, includes: p => p.Category);
}
// Yeni ürün ekle
public async Task AddProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
await repo.AddAsync(product);
await _unitOfWork.SaveChangesAsync();
}
// Ürün güncelle
public async Task UpdateProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Update(product);
await _unitOfWork.SaveChangesAsync();
}
// Yumuşak silme (SoftDelete)
public async Task SoftDeleteProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Delete(product); // IsDeleted = true olarak işaretler
await _unitOfWork.SaveChangesAsync();
}
// Kalıcı silme (Hard Delete)
public async Task HardDeleteProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Delete(product, hardDelete: true);
await _unitOfWork.SaveChangesAsync();
}
}
SoftDelete Kullanımı
Entity sınıfınızda IsDeleted property'si olmalıdır:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool IsDeleted { get; set; } // SoftDelete için gerekli
// Özel property adı kullanılabilir:
// public bool Silindi { get; set; }
}
// Varsayılan "IsDeleted" property ile
repo.Delete(product); // SoftDelete
// Özel property adı ile
repo.Delete(product, isDeletedPropertyName: "Silindi");
// Silinmiş kayıtlar dahil listeleme
var allProducts = await repo.GetAllAsync(softDeletedIncluded: true);
Sayfalama (Pagination)
// LINQ tabanlı sayfalama
var pagedProducts = await repo.GetPaginationListAsync(
predicate: p => p.Price > 50,
orderBy: q => q.OrderByDescending(p => p.Name),
include: q => q.Include(p => p.Category),
index: 0, // Sayfa numarası (0 tabanlı)
size: 10 // Sayfa boyutu
);
// Sonuçları kullanma
int totalCount = pagedProducts.Count; // Toplam kayıt sayısı
int totalPages = pagedProducts.Pages; // Toplam sayfa sayısı
bool hasNext = pagedProducts.HasNext; // Sonraki sayfa var mı?
bool hasPrev = pagedProducts.HasPrevious; // Önceki sayfa var mı?
var items = pagedProducts.Items; // Mevcut sayfadaki kayıtlar
SQL Function ve Stored Procedure
// SQL Function ile veri çekme (pagination ile)
var result = await repo.GetPaginationListFromFunctionAsync(
functionName: "fn_GetActiveProducts",
parameters: new { CategoryId = 5 },
index: 0,
size: 10
);
// SQL Function ile tüm verileri çekme (pagination olmadan)
var allData = await repo.GetAllFromFunctionAsync(
functionName: "fn_GetActiveProducts",
parameters: new { CategoryId = 5 }
);
// SQL Function + Where koşulu
var filtered = await repo.GetAllFromFunctionWithWhereAsync(
functionName: "fn_GetProducts",
functionParameters: new { Year = 2025 },
predicate: p => p.Price > 100,
orderBy: q => q.OrderByDescending(p => p.Name)
);
// Stored Procedure ile veri çekme
var spResult = await repo.GetAllFromStoredProcedureAsync(
storedProcedureName: "sp_GetProductsByCategory",
parameters: new { CategoryId = 5 }
);
// Raw SQL çalıştırma
var sqlResult = await repo.QuerySqlAsync(
"SELECT * FROM Products WHERE Price > @p0", 100
);
// Scalar değer döndürme
var count = await repo.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM Products WHERE IsDeleted = 0"
);
Transaction Yönetimi
_unitOfWork.BeginTransaction();
try
{
var productRepo = _unitOfWork.GetRepository<Product, int>();
var logRepo = _unitOfWork.GetRepository<AuditLog, int>();
await productRepo.AddAsync(newProduct);
await logRepo.AddAsync(new AuditLog { Action = "ProductAdded" });
await _unitOfWork.CommitTransactionAsync();
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
Aggregate Metotları
var repo = _unitOfWork.GetRepository<Product, int>();
// Sayma
int count = await repo.CountAsync(p => p.Price > 100);
// Toplam
decimal total = await repo.SumAsync(p => p.Price, predicate: p => p.IsActive);
// Ortalama
decimal avg = await repo.AverageAsync(p => p.Price);
// Minimum / Maximum
decimal min = await repo.MinAsync(p => p.Price, predicate: p => p.CategoryId == 1);
decimal max = await repo.MaxAsync(p => p.Price);
// Varlık kontrolü
bool exists = await repo.ExistsAsync(p => p.Name == "Test");
Bulk İşlemler
var repo = _unitOfWork.GetRepository<Product, int>();
// Toplu ekleme
await repo.BulkInsertAsync(productList);
// Toplu güncelleme
await repo.BulkUpdateAsync(productList);
// Toplu silme (soft delete)
await repo.BulkDeleteAsync(productList);
// Toplu silme (hard delete)
await repo.BulkDeleteAsync(productList, hardDelete: true);
Dinamik Veritabanı Değiştirme
// Context'i farklı bir veritabanı ile başlat
_unitOfWork.InitializeContext("SecondDatabase");
var repo = _unitOfWork.GetRepositoryOutDbName<Product, int>();
var products = await repo.GetAllAsync();
🇬🇧 English Documentation
Overview
EFGenericUnitOfWork is a general-purpose data access library built on top of Entity Framework Core, providing Repository Pattern, Unit of Work Pattern, SoftDelete, and Pagination support.
What Does It Do?
- ✅ Abstracts database operations for clean architecture
- ✅ Centralized CRUD operations via Generic Repository
- ✅ SoftDelete support — marks records instead of physical deletion
- ✅ Built-in Pagination support
- ✅ Transaction management
- ✅ SQL Function and Stored Procedure support
- ✅ Optimized Bulk operations
- ✅ Multi-database support (
RepositoryOutDbName) - ✅ Both synchronous and asynchronous methods
Installation
dotnet add package EFGenericUnitOfWork
DI Registration (Dependency Injection)
// Program.cs or Startup.cs
// 1. Standard usage (single database)
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddScoped<IUnitOfWork>(provider =>
new UnitOfWork<AppDbContext>(provider.GetRequiredService<AppDbContext>()));
// 2. Dynamic database usage (multiple databases)
builder.Services.AddScoped<IDbContextFactory<AppDbContext>, AppDbContextFactory>();
builder.Services.AddScoped<IUnitOfWork>(provider =>
new UnitOfWork<AppDbContext>(provider.GetRequiredService<IDbContextFactory<AppDbContext>>()));
Basic Usage Examples
CRUD Operations with Repository
public class ProductService
{
private readonly IUnitOfWork _unitOfWork;
public ProductService(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
// Get all products
public async Task<IEnumerable<Product>> GetAllProductsAsync()
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetAllAsync();
}
// Get with filtering, sorting and includes
public async Task<IEnumerable<Product>> GetActiveProductsAsync()
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetAllAsync(
predicate: p => p.Price > 100,
orderBy: q => q.OrderByDescending(p => p.CreatedDate),
include: q => q.Include(p => p.Category),
asNoTracking: true
);
}
// Get by ID
public async Task<Product> GetProductByIdAsync(int id)
{
var repo = _unitOfWork.GetRepository<Product, int>();
return await repo.GetByIdAsync(id, includes: p => p.Category);
}
// Add new product
public async Task AddProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
await repo.AddAsync(product);
await _unitOfWork.SaveChangesAsync();
}
// Update product
public async Task UpdateProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Update(product);
await _unitOfWork.SaveChangesAsync();
}
// Soft Delete
public async Task SoftDeleteProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Delete(product); // Sets IsDeleted = true
await _unitOfWork.SaveChangesAsync();
}
// Hard Delete
public async Task HardDeleteProductAsync(Product product)
{
var repo = _unitOfWork.GetRepository<Product, int>();
repo.Delete(product, hardDelete: true);
await _unitOfWork.SaveChangesAsync();
}
}
SoftDelete Usage
Your entity class must have an IsDeleted property:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public bool IsDeleted { get; set; } // Required for SoftDelete
// Custom property name is also supported:
// public bool Deleted { get; set; }
}
// Default "IsDeleted" property
repo.Delete(product); // SoftDelete
// Custom property name
repo.Delete(product, isDeletedPropertyName: "Deleted");
// Include soft-deleted records in listing
var allProducts = await repo.GetAllAsync(softDeletedIncluded: true);
Pagination
// LINQ-based pagination
var pagedProducts = await repo.GetPaginationListAsync(
predicate: p => p.Price > 50,
orderBy: q => q.OrderByDescending(p => p.Name),
include: q => q.Include(p => p.Category),
index: 0, // Page number (0-based)
size: 10 // Page size
);
// Using results
int totalCount = pagedProducts.Count; // Total record count
int totalPages = pagedProducts.Pages; // Total page count
bool hasNext = pagedProducts.HasNext; // Has next page?
bool hasPrev = pagedProducts.HasPrevious; // Has previous page?
var items = pagedProducts.Items; // Items on current page
SQL Functions and Stored Procedures
// SQL Function with pagination
var result = await repo.GetPaginationListFromFunctionAsync(
functionName: "fn_GetActiveProducts",
parameters: new { CategoryId = 5 },
index: 0,
size: 10
);
// SQL Function without pagination
var allData = await repo.GetAllFromFunctionAsync(
functionName: "fn_GetActiveProducts",
parameters: new { CategoryId = 5 }
);
// SQL Function + Where clause
var filtered = await repo.GetAllFromFunctionWithWhereAsync(
functionName: "fn_GetProducts",
functionParameters: new { Year = 2025 },
predicate: p => p.Price > 100,
orderBy: q => q.OrderByDescending(p => p.Name)
);
// Stored Procedure
var spResult = await repo.GetAllFromStoredProcedureAsync(
storedProcedureName: "sp_GetProductsByCategory",
parameters: new { CategoryId = 5 }
);
// Raw SQL execution
var sqlResult = await repo.QuerySqlAsync(
"SELECT * FROM Products WHERE Price > @p0", 100
);
// Scalar value
var count = await repo.ExecuteScalarAsync<int>(
"SELECT COUNT(*) FROM Products WHERE IsDeleted = 0"
);
Transaction Management
_unitOfWork.BeginTransaction();
try
{
var productRepo = _unitOfWork.GetRepository<Product, int>();
var logRepo = _unitOfWork.GetRepository<AuditLog, int>();
await productRepo.AddAsync(newProduct);
await logRepo.AddAsync(new AuditLog { Action = "ProductAdded" });
await _unitOfWork.CommitTransactionAsync();
}
catch
{
await _unitOfWork.RollbackTransactionAsync();
throw;
}
Aggregate Methods
var repo = _unitOfWork.GetRepository<Product, int>();
// Count
int count = await repo.CountAsync(p => p.Price > 100);
// Sum
decimal total = await repo.SumAsync(p => p.Price, predicate: p => p.IsActive);
// Average
decimal avg = await repo.AverageAsync(p => p.Price);
// Min / Max
decimal min = await repo.MinAsync(p => p.Price, predicate: p => p.CategoryId == 1);
decimal max = await repo.MaxAsync(p => p.Price);
// Exists
bool exists = await repo.ExistsAsync(p => p.Name == "Test");
Bulk Operations
var repo = _unitOfWork.GetRepository<Product, int>();
// Bulk insert
await repo.BulkInsertAsync(productList);
// Bulk update
await repo.BulkUpdateAsync(productList);
// Bulk soft delete
await repo.BulkDeleteAsync(productList);
// Bulk hard delete
await repo.BulkDeleteAsync(productList, hardDelete: true);
Dynamic Database Switching
// Initialize context with a different database
_unitOfWork.InitializeContext("SecondDatabase");
var repo = _unitOfWork.GetRepositoryOutDbName<Product, int>();
var products = await repo.GetAllAsync();
API Reference / API Referansı
| Interface | Description (EN) | Açıklama (TR) |
|---|---|---|
IRepository<T, TId> |
Generic repository with CRUD, pagination, aggregation | CRUD, sayfalama, toplama metotları içeren genel repository |
IRepositoryOutDbName<T, TId> |
Repository with dynamic database switching | Dinamik veritabanı değiştirme destekli repository |
IUnitOfWork |
Unit of Work with transaction support | Transaction destekli Unit of Work |
IDbContextFactory<TContext> |
Factory for creating DbContext with database name | Veritabanı adı ile DbContext oluşturma fabrikası |
IPaginate<T> |
Pagination result model | Sayfalama sonuç modeli |
PageRequest |
Pagination request model | Sayfalama istek modeli |
Dependencies / Bağımlılıklar
Microsoft.EntityFrameworkCore.Abstractions(10.0.5)Microsoft.EntityFrameworkCore.Design(10.0.5)Microsoft.EntityFrameworkCore.SqlServer(10.0.5)Microsoft.EntityFrameworkCore.Tools(10.0.5)
| 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.Abstractions (>= 10.0.5)
- Microsoft.EntityFrameworkCore.SqlServer (>= 10.0.5)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on EFGenericUnitOfWork:
| Package | Downloads |
|---|---|
|
EmfGlobalCaching
A global caching library with support for MediatR, Memory Caching, and Redis Caching, enhancing performance and scalability. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.44 | 103 | 4/27/2026 |
| 1.0.43 | 97 | 4/20/2026 |
| 1.0.42 | 123 | 3/16/2026 |
| 1.0.41 | 214 | 7/27/2025 |
| 1.0.40 | 195 | 7/27/2025 |
| 1.0.39 | 194 | 7/27/2025 |
| 1.0.38 | 206 | 7/26/2025 |
| 1.0.37 | 227 | 7/26/2025 |
| 1.0.36 | 242 | 7/26/2025 |
| 1.0.35 | 281 | 7/26/2025 |
| 1.0.34 | 292 | 7/26/2025 |
| 1.0.33 | 287 | 7/26/2025 |
| 1.0.32 | 467 | 3/24/2025 |
| 1.0.31 | 341 | 3/23/2025 |
| 1.0.30 | 255 | 3/11/2025 |
| 1.0.29 | 250 | 3/11/2025 |
| 1.0.28 | 295 | 3/5/2025 |
| 1.0.27 | 190 | 2/18/2025 |
| 1.0.26 | 180 | 2/18/2025 |
| 1.0.25 | 185 | 2/17/2025 |
Initial release with support for Entity Framework Core, Repository Pattern, UnitOfWork Pattern, SoftDelete functionality, paging, and synchronous/asynchronous support.