Ana içeriğe geç

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 |