This console application demonstrates the complete feature set of CSharpEssentials.EntityFrameworkCore including soft delete, audit interceptors, pagination, enum conversion, and naming conventions.
| Feature | File | Description |
|---|---|---|
| BaseDbContext | Data/ShopDbContext.cs |
Automatic configuration discovery, soft-delete filters |
| Soft Delete | Data/Entities/Product.cs |
Entities are flagged, not removed; filtered from queries |
| Audit Interceptor | Built into BaseDbContext | Auto-sets CreatedAt and UpdatedAt |
| Domain Event Interceptor | Built into BaseDbContext | Dispatches entity domain events BeforeSave / AfterSave |
| Pagination | Services/ProductCatalogService.cs |
Offset-based ToPaginatedList() and cursor-based PaginateAsync() |
| Enum to String | Data/ShopDbContext.cs |
Stores enums as snake_case strings in the database |
| Snake Case Naming | Data/ShopDbContext.cs |
Automatic PascalCase -> snake_case conversion |
cd examples/Examples.EntityFrameworkCore
dotnet runThe demo performs the following steps automatically:
- Database Creation — Creates a local SQLite database (
shop.db). - Seeding — Inserts 3 initial products.
- Soft Delete Demo — Deletes the "Wireless Mouse" product, then shows:
- Visible products (filtered): 2
- Total products (with deleted): 3
- Pagination Demo — Adds 25 more products and demonstrates page navigation.
- Enum Conversion Demo — Shows how
ProductCategoryis stored as strings. - Audit Interceptor Demo — Modifies a product and observes
UpdatedAtbeing set automatically. - Cursor Pagination Demo — Efficient pagination for large datasets using
CursorPaginationRequest<T>. - Domain Event Interceptor Demo — Dispatches events raised by entities during
SaveChanges.
ShopDbContext inherits from BaseDbContext:
public class ShopDbContext : BaseDbContext<ShopDbContext>
{
public ShopDbContext(
DbContextOptions<ShopDbContext> options,
IServiceScopeFactory serviceScopeFactory) : base(options, serviceScopeFactory) { }
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
base.ConfigureConventions(configurationBuilder);
configurationBuilder.ConfigureEnumConventions(typeof(ShopDbContext).Assembly);
}
}public class Product : SoftDeletableEntityBase
{
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public ProductCategory Category { get; set; }
}Deleting a product:
_dbContext.Products.Remove(product);
_dbContext.SaveChanges();
// Product.IsDeleted = true, row remains in databaseQuerying without soft-delete filter:
var all = db.Products.IgnoreQueryFilters().ToList();var request = new PaginationRequest { PageNumber = 1, PageSize = 10 };
var page = await _dbContext.Products
.OrderBy(p => p.Name)
.PaginateAsync(request);
// page.Items -> IReadOnlyList<Product> for current page
// page.TotalCount -> Total items across all pages
// page.TotalPages -> Total number of pages
// page.HasNextPage -> boolIdeal for infinite scroll or very large datasets.
var request = new CursorPaginationRequest<DateTimeOffset>
{
Limit = 20,
Cursor = lastSeenDate
};
var response = await _dbContext.Logs
.PaginateAsync(request, cursorSelector: x => x.CreatedAt);
// response.Next -> Continue from hereThe EnumToStringConverter stores enum values as human-readable strings:
| Enum Value | Stored in DB |
|---|---|
ProductCategory.Electronics |
"electronics" |
ProductCategory.Clothing |
"clothing" |
ProductCategory.Food |
"food" |
This makes the database self-documenting and avoids magic numbers.