EfTune
EfTune is a lightweight, extensible EF Core enhancement toolkit that brings interceptors, UTC conversion, auditing, value object support, domain event dispatching, soft delete handling, and more — all designed with clean architecture and modular design in mind.
"Enhance EF Core without the bloat. Just tune what you need."
Features
ISaveChangesInterceptor-based composite pipeline- Automatic UTC Conversion with
[AutoUtc]support - Audit Fields (
CreatedAt,UpdatedAt,CreatedBy,UpdatedBy) - Soft Delete Interceptor + Global Query Filters
- Value Object Conversion (
HasConversion<TValueObject, Primitive>) - Seed Helpers (
SeedIfEmpty,SeedFromEnum) - Global Query Filters for interfaces like
ITenantScoped - RowVersion / Concurrency Token Automation
- Domain Event Dispatcher with outbox compatibility
- Minimal setup, maximum flexibility
Installation
dotnet add package Moongazing.EfTune
Setup
In your Program.cs or Startup.cs:
services.AddEfTune(x => x
.EnableUtc()
.EnableAudit()
.EnableSoftDelete()
.EnableDomainEvents()
);
If you're using domain events:
services.AddScoped<IDomainEventPublisher, YourEventPublisher>();
Interfaces
IAuditableEntity
public interface IAuditableEntity
{
DateTime CreatedAt { get; set; }
DateTime UpdatedAt { get; set; }
string? CreatedBy { get; set; }
string? UpdatedBy { get; set; }
}
ISoftDeletable
public interface ISoftDeletable
{
bool IsDeleted { get; set; }
}
IHasRowVersion
public interface IHasRowVersion
{
byte[] RowVersion { get; set; }
}
IDomainEvent & IHasDomainEvents
public interface IDomainEvent
{
DateTime OccurredOn { get; }
}
public interface IHasDomainEvents
{
List<IDomainEvent> DomainEvents { get; }
void ClearDomainEvents() => DomainEvents.Clear();
}
ModelBuilder Extensions
In your DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.UseSoftDelete()
.UseGlobalFilter<ITenantScoped>(e => e.TenantId == tenantId)
.UseConcurrencyTokens();
}
Value Object Mapping
modelBuilder.Entity<User>()
.HasSimpleValueObjectConversion(u => u.Email);
Or custom:
modelBuilder.Entity<User>()
.HasValueObjectConversion(
u => u.Email,
vo => vo.Value,
val => new Email(val)
);
Seeding
await db.SeedIfEmptyAsync(new[]
{
new Category { Id = 1, Name = "Books" }
});
await db.SeedFromEnumAsync<RoleType, Role>(e => new Role
{
Id = (int)(object)e,
Name = e.ToString()
});
Domain Events
Entity
public class Order : IHasDomainEvents
{
public List<IDomainEvent> DomainEvents { get; } = new();
public void AddCreatedEvent() => DomainEvents.Add(new OrderCreated(Id));
}
Event
public record OrderCreated(Guid OrderId) : IDomainEvent
{
public DateTime OccurredOn { get; } = DateTime.UtcNow;
}
Publisher (example)
public class ConsolePublisher : IDomainEventPublisher
{
public Task PublishAsync(IDomainEvent @event, CancellationToken ct = default)
{
Console.WriteLine($"Published: {@event.GetType().Name}");
return Task.CompletedTask;
}
}
Philosophy
EfTune is built with:
Clean architecture principles
Modularity & composability
Convention over configuration
Production-readiness with minimal friction
Roadmap
Version Features
v1.0 Interceptor system, UTC, Audit, Seed, ValueObject
v1.1 Soft Delete, QueryFilter, RowVersion, Domain Events
v1.2 Outbox Dispatcher, Change Log Interceptor, Dev Logger
v2.0 CLI Tooling, Background Processor, Telemetry Integration
License
MIT © Tunahan Ali Öztürk
GitHub
https://github.com/Moongazing/EfTune
## Packages
| Package | Version | Downloads |
| --- | --- | --- |
| [EfTune](https://www.nuget.org/packages/EfTune) | 1.0.0 | 223 |