Diiwo.Core
0.2.0
dotnet add package Diiwo.Core --version 0.2.0
NuGet\Install-Package Diiwo.Core -Version 0.2.0
<PackageReference Include="Diiwo.Core" Version="0.2.0" />
<PackageVersion Include="Diiwo.Core" Version="0.2.0" />
<PackageReference Include="Diiwo.Core" />
paket add Diiwo.Core --version 0.2.0
#r "nuget: Diiwo.Core, 0.2.0"
#:package Diiwo.Core@0.2.0
#addin nuget:?package=Diiwo.Core&version=0.2.0
#tool nuget:?package=Diiwo.Core&version=0.2.0
Diiwo.Core
[](https://github.com/diiwo/diiwo-core/actions
Universal base entities, audit trails, and core functionality for all .NET applications.
Diiwo.Core is a lightweight, universal library that provides base entities, automatic audit trails, soft delete functionality, and essential interfaces for building robust .NET applications. Designed to be framework-agnostic and highly reusable across web, console, desktop, and service applications.
โจ Key Features
Core Entities & Audit
- ๐๏ธ Universal Base Entities -
BaseEntity,AuditableEntity,UserTrackedEntity,DomainEntity - ๐ Zero-Code Audit Trails - Automatic audit field population with zero manual code required
- ๐๏ธ Enterprise Soft Delete - Preserve data for compliance while marking as terminated
- ๐ Entity State Management - Complete lifecycle tracking (
Created,Active,Inactive,Terminated) - ๐ค User Attribution - Automatic tracking of who made changes and when
- ๐ข Compliance Ready - Meet regulatory requirements with comprehensive audit trails
API Response Patterns (New in v0.2.0)
- ๐ฆ ApiResponse<T> - Standardized API response wrapper for consistent formatting
- ๐ PagedResponse<T> - Built-in pagination support with metadata
- โ ValidationResponse - Structured validation error responses
Exception Handling (New in v0.2.0)
- โ ๏ธ BusinessException - Base exception for business logic violations
- ๐ NotFoundException - Resource not found exceptions
- ๐ UnauthorizedException - Authentication/authorization failures
- โ๏ธ ValidationException - Input validation errors with field-level details
- โก ConflictException - Data conflict exceptions
Architecture & Compatibility
- ๐๏ธ Clean Architecture Ready - Follows DDD and clean architecture principles
- ๐ Framework Agnostic - Works with any .NET application type (Web, Console, Desktop, Services)
- โก Minimal Dependencies - Only essential EF Core and DI dependencies
- ๐งช Production Proven - Powers enterprise solutions like Diiwo.Identity
๐ Quick Start
Installation
Option 1: NuGet.org (Recommended)
# Install via .NET CLI
dotnet add package Diiwo.Core
# Install via Package Manager Console
Install-Package Diiwo.Core
# Install via PackageReference
<PackageReference Include="Diiwo.Core" Version="0.2.0" />
Option 2: GitHub Packages
# Add GitHub Packages as a source
dotnet nuget add source https://nuget.pkg.github.com/Diiwo/index.json --name github --username YOUR_GITHUB_USERNAME --password YOUR_GITHUB_TOKEN
# Install via .NET CLI
dotnet add package Diiwo.Core --version 0.2.0
Option 3: Project Reference
<ProjectReference Include="..\DIIWO-Core\DIIWO.Core.csproj" />
Option 4: GitHub Release
Download the .nupkg file from Releases and install locally:
dotnet add package Diiwo.Core --source /path/to/downloaded/packages
Basic Setup
// Program.cs or Startup.cs
using Diiwo.Core.Extensions;
// Register Diiwo.Core with your CurrentUserService implementation
services.AddDiiwoCore<MyCurrentUserService>();
// Or if you already have ICurrentUserService registered elsewhere
services.AddDiiwoCoreWithExistingUserService();
Your Entity
using Diiwo.Core.Domain.Entities;
// Simple entity with basic audit fields
public class Product : AuditableEntity
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
// IsActive, CreatedAt, UpdatedAt, State automatically included!
}
// Entity with user tracking
public class Order : UserTrackedEntity
{
public string OrderNumber { get; set; }
public decimal Total { get; set; }
// Includes all AuditableEntity fields PLUS CreatedBy, UpdatedBy
}
// Multi-tenant entity
public class Document : UserOwnedEntity
{
public string Title { get; set; }
public string Content { get; set; }
// Includes UserTrackedEntity fields PLUS UserId for ownership
}
DbContext Setup
using Diiwo.Core.Interceptors;
using Microsoft.EntityFrameworkCore;
public class MyDbContext : DbContext
{
private readonly AuditInterceptor _auditInterceptor;
public MyDbContext(DbContextOptions<MyDbContext> options, AuditInterceptor auditInterceptor)
: base(options)
{
_auditInterceptor = auditInterceptor;
}
public DbSet<Product> Products { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// Add audit interceptor for automatic audit trail population
optionsBuilder.AddInterceptors(_auditInterceptor);
}
}
๐ฏ Zero-Code Enterprise Audit Trail
// โ
Enterprise-grade auditing with ZERO manual code!
var product = new Product
{
Name = "iPhone 15",
Price = 999.00m,
Description = "Latest iPhone"
// โ
NO audit code needed - everything automatic!
};
context.Products.Add(product);
await context.SaveChangesAsync(); // ๐ฏ Triggers AuditInterceptor
// โ
Automatically populated by Diiwo.Core:
// product.Id = Guid.NewGuid()
// product.CreatedAt = DateTime.UtcNow
// product.UpdatedAt = DateTime.UtcNow
// product.State = EntityState.Active
// product.CreatedBy = currentUserId (from ICurrentUserService)
// product.UpdatedBy = currentUserId
// โ
Enterprise benefits:
// - Complete audit trail for compliance
// - User attribution for security
// - State management for lifecycle tracking
// - Soft delete preserves data history
๐๏ธ Architecture Overview
Base Entity Hierarchy
BaseEntity (Id, CreatedAt, UpdatedAt)
โโโ AuditableEntity (+ State, Soft Delete)
โโโ UserTrackedEntity (+ CreatedBy, UpdatedBy)
โโโ DomainEntity (for business entities)
โโโ UserOwnedEntity (+ UserId for multi-tenant)
Entity States
public enum EntityState
{
Created = 0, // Created but not yet active
Inactive = 1, // Temporarily inactive
Active = 2, // Active and available
Effective = 3, // Effective and operational
Terminated = 4 // Soft deleted
}
Entity State Management
Entities automatically transition through states during their lifecycle:
- Created - Initial state when entity is created
- Inactive - Temporarily disabled but can be reactivated
- Active - Normal operational state (default)
- Effective - Fully operational and effective
- Terminated - Soft deleted, preserves audit history
๐ Usage Examples
Implementing ICurrentUserService
// For ASP.NET Core applications
public class WebCurrentUserService : ICurrentUserService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public WebCurrentUserService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public Guid? UserId
{
get
{
var userIdClaim = _httpContextAccessor.HttpContext?.User?
.FindFirst(ClaimTypes.NameIdentifier)?.Value;
return Guid.TryParse(userIdClaim, out var userId) ? userId : null;
}
}
public string? UserName => _httpContextAccessor.HttpContext?.User?.Identity?.Name;
public string? UserEmail => _httpContextAccessor.HttpContext?.User?
.FindFirst(ClaimTypes.Email)?.Value;
public bool IsAuthenticated => _httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated ?? false;
public Task<bool> IsInRoleAsync(string role)
{
var user = _httpContextAccessor.HttpContext?.User;
return Task.FromResult(user?.IsInRole(role) ?? false);
}
}
// For Console/Service applications
public class SystemCurrentUserService : ICurrentUserService
{
public Guid? UserId => Guid.Parse("00000000-0000-0000-0000-000000000001"); // System user
public string? UserName => "System";
public string? UserEmail => "system@diiwo.com";
public bool IsAuthenticated => true;
public Task<bool> IsInRoleAsync(string role) => Task.FromResult(true);
}
Working with Soft Deletes
// Instead of hard delete, mark as terminated
product.SoftDelete(); // Sets State = EntityState.Terminated
await context.SaveChangesAsync();
// Query only active entities
var activeProducts = await context.Products
.Where(p => p.IsActive) // Built-in property
.ToListAsync();
// Include soft-deleted entities
var allProducts = await context.Products
.IgnoreQueryFilters()
.ToListAsync();
// Restore soft-deleted entity
product.Restore(); // Sets State = EntityState.Active
await context.SaveChangesAsync();
Entity State Control
// Deactivate entity temporarily
product.State = EntityState.Inactive;
await context.SaveChangesAsync();
// Check state in your business logic
if (!product.IsActive)
{
throw new InvalidOperationException("Product is not available");
}
// Reactivate entity
product.State = EntityState.Active;
await context.SaveChangesAsync();
Multi-Tenant Usage
// Create user-owned entity
var document = new Document
{
Title = "My Document",
Content = "...",
UserId = currentUserId // Automatically set by UserOwnedEntity
};
// Check ownership
if (document.IsOwnedBy(currentUserId))
{
// User can access this document
}
// Query user's documents
var userDocs = await context.Documents
.Where(d => d.UserId == currentUserId || d.IsGlobal)
.ToListAsync();
Using API Response Patterns (v0.2.0)
using Diiwo.Core.Responses;
// Success response
[HttpGet("{id}")]
public async Task<ActionResult> GetUser(Guid id)
{
var user = await _userService.GetByIdAsync(id);
if (user == null)
return NotFound(ApiResponse<User>.ErrorResponse("User not found"));
return Ok(ApiResponse<User>.SuccessResponse(user, "User retrieved successfully"));
}
// Paginated response
[HttpGet]
public async Task<ActionResult> GetUsers(int page = 1, int pageSize = 10)
{
var users = await _userService.GetPagedAsync(page, pageSize);
var totalCount = await _userService.CountAsync();
return Ok(PagedResponse<User>.Create(users, page, pageSize, totalCount));
}
// Validation response
[HttpPost]
public async Task<ActionResult> CreateUser(UserDto dto)
{
if (!ModelState.IsValid)
{
var errors = ModelState.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.Errors.Select(e => e.ErrorMessage).ToList()
);
return BadRequest(ValidationResponse.Create(errors));
}
// ... create user
}
Using Exception Classes (v0.2.0)
using Diiwo.Core.Exceptions;
// Service layer
public class UserService
{
public async Task<User> GetByIdAsync(Guid id)
{
var user = await _repository.FindAsync(id);
if (user == null)
throw new NotFoundException("User", id);
return user;
}
public async Task<User> CreateAsync(string email)
{
var existing = await _repository.FindByEmailAsync(email);
if (existing != null)
throw new ConflictException("User", "Email", email);
// ... create user
}
public async Task ValidateAsync(UserDto dto)
{
var errors = new Dictionary<string, List<string>>();
if (string.IsNullOrEmpty(dto.Email))
errors.Add(nameof(dto.Email), new List<string> { "Email is required" });
if (errors.Any())
throw new ValidationException(errors);
}
}
// Global exception handler middleware
public class ExceptionMiddleware
{
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (NotFoundException ex)
{
context.Response.StatusCode = 404;
await context.Response.WriteAsJsonAsync(
ApiResponse<object>.ErrorResponse(ex.Message)
);
}
catch (ValidationException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(
ValidationResponse.Create(ex.ValidationErrors)
);
}
catch (UnauthorizedException ex)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsJsonAsync(
ApiResponse<object>.ErrorResponse(ex.Message)
);
}
catch (ConflictException ex)
{
context.Response.StatusCode = 409;
await context.Response.WriteAsJsonAsync(
ApiResponse<object>.ErrorResponse(ex.Message)
);
}
catch (BusinessException ex)
{
context.Response.StatusCode = 400;
await context.Response.WriteAsJsonAsync(
ApiResponse<object>.ErrorResponse(ex.Message)
);
}
}
}
๐ง Advanced Configuration
Custom Audit Behavior
public class CustomAuditInterceptor : AuditInterceptor
{
public CustomAuditInterceptor(ICurrentUserService currentUserService)
: base(currentUserService) { }
protected override void UpdateAuditFields(DbContext? context)
{
base.UpdateAuditFields(context);
// Add custom audit logic here
foreach (var entry in context.ChangeTracker.Entries<IAuditable>())
{
if (entry.State == EntityState.Added)
{
// Custom creation logic
}
}
}
}
// Register custom interceptor
services.AddScoped<AuditInterceptor, CustomAuditInterceptor>();
Multiple DbContexts
// Each DbContext can have its own audit interceptor
services.AddDbContext<ProductDbContext>((provider, options) =>
{
var interceptor = provider.GetRequiredService<AuditInterceptor>();
options.UseSqlServer(connectionString).AddInterceptors(interceptor);
});
services.AddDbContext<OrderDbContext>((provider, options) =>
{
var interceptor = provider.GetRequiredService<AuditInterceptor>();
options.UseNpgsql(connectionString).AddInterceptors(interceptor);
});
๐ข Enterprise Identity Integration
Diiwo.Core powers the enterprise features in Diiwo.Identity, providing automatic audit trails for complete identity management solutions.
Real-World Example: User Management with Audit Trail
// Using Diiwo.Identity with Diiwo.Core automatic auditing
public class AppUser : DomainEntity // โ
Inherits full audit capabilities
{
public required string Email { get; set; }
public required string PasswordHash { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
// โ
CreatedAt, UpdatedAt, CreatedBy, UpdatedBy automatically managed!
// โ
Soft delete preserves user history for compliance
// โ
State management tracks user lifecycle
}
// Service layer - zero manual audit code needed!
public class UserService
{
public async Task<AppUser> CreateUserAsync(string email, string password)
{
var user = new AppUser
{
Email = email,
PasswordHash = hashPassword(password)
// โ
All audit fields populated automatically by AuditInterceptor!
};
_context.Users.Add(user);
await _context.SaveChangesAsync(); // Triggers automatic audit population
return user; // user.CreatedAt, CreatedBy, etc. are now populated
}
public async Task DeactivateUserAsync(Guid userId)
{
var user = await _context.Users.FindAsync(userId);
user.State = EntityState.Terminated; // Soft delete
await _context.SaveChangesAsync();
// โ
UpdatedAt and UpdatedBy automatically set!
// โ
User preserved for audit/compliance requirements
}
}
Enterprise Permission System with Audit
// Permission entities with full audit trail
public class AppPermission : DomainEntity
{
public required string Resource { get; set; }
public required string Action { get; set; }
// โ
Every permission change tracked automatically
}
public class AppUserPermission : DomainEntity
{
public Guid UserId { get; set; }
public Guid PermissionId { get; set; }
public bool IsGranted { get; set; }
// โ
Full audit trail for permission assignments
}
// Permission assignments automatically audited
await _permissionService.GrantUserPermissionAsync(userId, permissionId);
// โ
Who granted the permission and when - automatically tracked!
Compliance & Regulatory Requirements
// Query audit trail for compliance reports
var userAuditTrail = await _context.Users
.Where(u => u.Id == userId)
.Select(u => new AuditReport
{
EntityId = u.Id,
CreatedAt = u.CreatedAt,
CreatedBy = u.CreatedBy,
LastUpdatedAt = u.UpdatedAt,
LastUpdatedBy = u.UpdatedBy,
CurrentState = u.State,
IsActive = u.IsActive
})
.FirstOrDefaultAsync();
// Soft-deleted entities preserved for audit
var deletedUsers = await _context.Users
.IgnoreQueryFilters() // Include soft-deleted
.Where(u => u.State == EntityState.Terminated)
.ToListAsync();
// โ
Full history preserved for regulatory compliance
๐๏ธ Integration Examples
With Clean Architecture
// Domain Layer - Pure entities
public class Customer : DomainEntity
{
public string Name { get; private set; }
public Email Email { get; private set; }
// Domain methods
public void UpdateEmail(Email newEmail)
{
Email = newEmail;
// UpdatedAt and UpdatedBy automatically set on save
}
}
// Infrastructure Layer - DbContext
public class ApplicationDbContext : DbContext
{
// ... DbContext implementation with AuditInterceptor
}
With MediatR
public class CreateProductCommand : IRequest<Guid>
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class CreateProductHandler : IRequestHandler<CreateProductCommand, Guid>
{
private readonly ApplicationDbContext _context;
public async Task<Guid> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
var product = new Product
{
Name = request.Name,
Price = request.Price
// Audit fields automatically populated
};
_context.Products.Add(product);
await _context.SaveChangesAsync(cancellationToken);
return product.Id;
}
}
With Repository Pattern
public interface IRepository<T> where T : BaseEntity
{
Task<T?> GetByIdAsync(Guid id);
Task<IEnumerable<T>> GetActiveAsync();
Task AddAsync(T entity);
Task UpdateAsync(T entity);
Task SoftDeleteAsync(Guid id);
}
public class Repository<T> : IRepository<T> where T : AuditableEntity
{
private readonly DbContext _context;
public async Task<IEnumerable<T>> GetActiveAsync()
{
return await _context.Set<T>()
.Where(x => x.IsActive)
.ToListAsync();
}
public async Task SoftDeleteAsync(Guid id)
{
var entity = await GetByIdAsync(id);
if (entity != null)
{
entity.SoftDelete(); // Uses Diiwo.Core functionality
await _context.SaveChangesAsync();
}
}
}
๐ Framework Compatibility
| Framework | Support | Notes |
|---|---|---|
| ASP.NET Core | โ Full | Recommended for web applications |
| Console Apps | โ Full | Perfect for background services |
| Desktop (WPF/WinUI) | โ Full | Great for desktop applications |
| Blazor | โ Full | Works with both Server and WebAssembly |
| Worker Services | โ Full | Ideal for background processing |
| Azure Functions | โ Full | Serverless applications |
| Minimal APIs | โ Full | Lightweight web APIs |
๐ Performance
- Zero Runtime Overhead - Entities are POCOs with no runtime proxies
- Minimal Memory Footprint - Only essential properties added to base entities
- EF Core Optimized - Interceptors use EF Core's native change tracking
- Lazy Evaluation - Computed properties use lazy evaluation where possible
๐ ๏ธ Contributing
We welcome contributions! Please see our Contributing Guide for details.
Development Setup
# Clone the repository
git clone https://github.com/diiwo/diiwo-core.git
cd diiwo-core
# Restore dependencies
dotnet restore
# Build the project
dotnet build
# Run tests
dotnet test
๐ Related Projects
- Diiwo.Identity - Complete identity management with dual architectures
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Support
- ๐ Documentation: Getting Started Guide
- ๐๏ธ Architecture: Architecture Guide
- ๐ Issues: GitHub Issues
- ๐ฌ Discussions: GitHub Discussions
<div align="center">
Built with โค๏ธ by the DIIWO Team
</div>
| 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.0)
- Microsoft.EntityFrameworkCore.Abstractions (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection (>= 8.0.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 8.0.0)
- System.ComponentModel.Annotations (>= 5.0.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Diiwo.Core:
| Package | Downloads |
|---|---|
|
Diiwo.Identity
Dual-architecture user management and authentication library for Diiwo applications. Choose between App (simple) or AspNet (enterprise) architectures. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 0.2.0 - Added Response Patterns and Exceptions:
โข Added ApiResponse<T> for standardized API responses
โข Added PagedResponse<T> for paginated data
โข Added ValidationResponse for validation errors
โข Added standard exceptions (BusinessException, NotFoundException, UnauthorizedException, ValidationException, ConflictException)
โข Improved error handling patterns
Version 0.1.0 - Initial Release:
โข Universal base entities (BaseEntity, AuditableEntity, UserTrackedEntity, DomainEntity, UserOwnedEntity)
โข Automatic audit trail with AuditInterceptor
โข Built-in soft delete functionality
โข Entity state management and locking
โข Multi-tenant support with UserOwnedEntity
โข Framework-agnostic design with minimal dependencies
โข Full Entity Framework Core integration
โข Comprehensive documentation and examples