EfTune 1.0.0

dotnet add package EfTune --version 1.0.0
                    
NuGet\Install-Package EfTune -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EfTune" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EfTune" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="EfTune" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EfTune --version 1.0.0
                    
#r "nuget: EfTune, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EfTune@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EfTune&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=EfTune&version=1.0.0
                    
Install as a Cake Tool

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
Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 94 7/4/2025