Chd.AutoUI.EF
8.0.3
dotnet add package Chd.AutoUI.EF --version 8.0.3
NuGet\Install-Package Chd.AutoUI.EF -Version 8.0.3
<PackageReference Include="Chd.AutoUI.EF" Version="8.0.3" />
<PackageVersion Include="Chd.AutoUI.EF" Version="8.0.3" />
<PackageReference Include="Chd.AutoUI.EF" />
paket add Chd.AutoUI.EF --version 8.0.3
#r "nuget: Chd.AutoUI.EF, 8.0.3"
#:package Chd.AutoUI.EF@8.0.3
#addin nuget:?package=Chd.AutoUI.EF&version=8.0.3
#tool nuget:?package=Chd.AutoUI.EF&version=8.0.3
Chd.AutoUI.EF – Generic Repository for EF Core
Stop writing the same CRUD data-access code for every entity. Register once, use everywhere.
📝 Table of Contents
- About
- Features
- Installation
- Quick Start
- IGenericRepository Interface
- Filtering with FindAsync
- Extending the Repository
- Related Projects
About
Chd.AutoUI.EF provides a generic repository implementation built on EF Core. It is designed to pair with Chd.AutoUI but works independently in any ASP.NET Core project.
The motivation is simple: in CRUD-heavy applications you end up writing the same data-access code over and over — GetAll, GetById, Add, Update, Delete. This package puts all of that behind a single generic interface. You register it once and DI takes care of injecting the right DbSet<T> for each entity.
🎯 Features
- One registration covers all entity types via open generics (
IGenericRepository<>) - Full async support — all methods return
Task FindAsyncaccepts LINQ expressions for filtered queries without raw SQL- Works with any EF Core-supported database (PostgreSQL, SQL Server, SQLite, MySQL, etc.)
- Supports different ID types:
int,long,Guid, or any other type EF Core understands GenericRepository<T>is designed to be extended — inherit and add entity-specific methods
📦 Installation
dotnet add package Chd.AutoUI.EF
EF Core and a database provider are required separately:
dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL # PostgreSQL
# or
dotnet add package Microsoft.EntityFrameworkCore.SqlServer # SQL Server
# or
dotnet add package Microsoft.EntityFrameworkCore.Sqlite # SQLite
🚀 Quick Start
1. Create Your Entity
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public int Stock { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedDate { get; set; } = DateTime.UtcNow;
}
2. Register in Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
// One line registers IGenericRepository<T> for any entity type
builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
3. Inject and Use in a Controller
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
private readonly IGenericRepository<Product> _repo;
public ProductsController(IGenericRepository<Product> repo)
{
_repo = repo;
}
[HttpGet]
public async Task<IActionResult> GetAll() =>
Ok(await _repo.GetAllAsync());
[HttpGet("{id}")]
public async Task<IActionResult> GetById(int id)
{
var item = await _repo.GetByIdAsync(id);
return item == null ? NotFound() : Ok(item);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] Product product) =>
CreatedAtAction(nameof(GetById), new { id = product.Id }, await _repo.AddAsync(product));
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] Product product)
{
if (id != product.Id) return BadRequest();
return Ok(await _repo.UpdateAsync(product));
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id) =>
await _repo.DeleteAsync(id) ? Ok() : NotFound();
}
The same controller pattern applies to every other entity — just change the type parameter.
🔌 IGenericRepository Interface
public interface IGenericRepository<TEntity> where TEntity : class
{
/// <summary>Returns the entity with the given primary key, or null if not found.</summary>
Task<TEntity?> GetByIdAsync(object id);
/// <summary>Returns all records in the table.</summary>
Task<List<TEntity>> GetAllAsync();
/// <summary>Returns records matching the given LINQ expression.</summary>
Task<List<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>Adds a new record and returns the saved entity (with generated Id).</summary>
Task<TEntity> AddAsync(TEntity entity);
/// <summary>Updates an existing record and returns the saved entity.</summary>
Task<TEntity> UpdateAsync(TEntity entity);
/// <summary>Deletes the record with the given primary key. Returns true if deleted, false if not found.</summary>
Task<bool> DeleteAsync(object id);
/// <summary>Returns the total row count for the table.</summary>
Task<int> CountAsync();
}
🔍 Filtering with FindAsync
FindAsync accepts any LINQ predicate expression, so you can do most queries without stepping outside the repository:
// Active products with stock above a threshold
var available = await _repo.FindAsync(p => p.IsActive && p.Stock > 0);
// By exact match
var electronics = await _repo.FindAsync(p => p.CategoryId == 3);
// Contains search on a string field
var results = await _repo.FindAsync(p => p.Name.Contains("phone"));
// Combine conditions
var featured = await _repo.FindAsync(p => p.IsActive && p.IsFeatured && p.Price < 1000);
For queries that require Include, custom ordering, or pagination, extend the repository (see below).
🔧 Extending the Repository
When you need queries beyond what FindAsync covers, inherit from GenericRepository<T>:
public class ProductRepository : GenericRepository<Product>
{
public ProductRepository(AppDbContext context) : base(context) { }
public async Task<List<Product>> GetLowStockAsync(int threshold) =>
await _dbSet
.Where(p => p.IsActive && p.Stock < threshold)
.OrderBy(p => p.Stock)
.ToListAsync();
public async Task<List<Product>> GetByCategoryAsync(int categoryId) =>
await _dbSet
.Include(p => p.Category)
.Where(p => p.CategoryId == categoryId && p.IsActive)
.ToListAsync();
public async Task<(List<Product> Items, int Total)> GetPagedAsync(int page, int pageSize) =>
(
await _dbSet.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync(),
await _dbSet.CountAsync()
);
}
Register the custom repository for Product specifically — DI will use it for that type and fall back to the generic implementation for everything else:
// Specific entity uses the extended repository
builder.Services.AddScoped<IGenericRepository<Product>, ProductRepository>();
// All other entities use the generic implementation
builder.Services.AddScoped(typeof(IGenericRepository<>), typeof(GenericRepository<>));
🔗 Related Projects
| Project | Description |
|---|---|
| Chd.AutoUI | Attribute-driven UI metadata for .NET — works great alongside this package |
| chd-auto-ui-react | React components for dynamic forms and grids |
| AutoUI Demo | Full POS demo using both packages together |
| All Demo Projects | All CHD examples, benchmarks and test projects |
📄 License
MIT
| 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.11)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.