EFFluentify.Tool 1.0.2

dotnet tool install --global EFFluentify.Tool --version 1.0.2
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local EFFluentify.Tool --version 1.0.2
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=EFFluentify.Tool&version=1.0.2
                    
nuke :add-package EFFluentify.Tool --version 1.0.2
                    

EFFluentify

EFFluentify is a .NET CLI tool that converts Entity Framework Core Data Annotations into equivalent Fluent API configurations. Point it at your entity classes and it generates clean IEntityTypeConfiguration<T> classes, optionally stripping the now-redundant annotations from your original source.

It parses your C# with Roslyn, so it understands your real types and relationships rather than matching strings.

Why

Data Annotations are convenient but scatter mapping concerns across your domain classes and can't express everything the Fluent API can. EFFluentify lets you keep the ergonomics of annotations while moving your configuration into dedicated, testable IEntityTypeConfiguration<T> classes — in one pass, across a whole project.

Example

Given an annotated entity:

[Table("Users", Schema = "dbo")]
[Index(nameof(Email), IsUnique = true, Name = "IX_User_Email")]
public class User
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    [Required, MaxLength(50)]
    public string Name { get; set; }

    public string? Email { get; set; }

    [NotMapped]
    public string TemporaryToken { get; set; }

    [ConcurrencyCheck]
    public string RowGuid { get; set; }

    public int? ManagerId { get; set; }
    [ForeignKey(nameof(ManagerId))]
    public User Manager { get; set; }
    public ICollection<User> Subordinates { get; set; }
}

EFFluentify emits:

// <auto-generated />
namespace EFFluentify.Configurations;

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

internal sealed class UserConfiguration : IEntityTypeConfiguration<User>
{
    public void Configure(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("Users", "dbo");
        builder.HasIndex(e => e.Email).IsUnique().HasDatabaseName("IX_User_Email");

        builder.Ignore(e => e.TemporaryToken);
        builder.HasOne(x => x.Manager).WithMany(x => x.Subordinates).HasForeignKey(x => x.ManagerId);

        builder.Property(x => x.Id).ValueGeneratedOnAdd();
        builder.Property(x => x.Name).IsRequired().HasMaxLength(50);
        builder.Property(x => x.Email).IsRequired(false);
        builder.Property(x => x.RowGuid).IsConcurrencyToken();
    }
}

Installation

Requires the .NET 9 SDK.

The project is packaged as a .NET tool (PackAsTool) with the command name effluentify. Pack and install it locally from the repository root:

dotnet pack EFFluentify.Cli -c Release
dotnet tool install --global --add-source ./EFFluentify.Cli/bin/Release EFFluentify.Tool

Or run it straight from source without installing:

dotnet run --project EFFluentify.Cli -- --input ./Models --out ./Configurations

Usage

effluentify --input <path> [--input <path> ...] [--out <dir>] [--manyFiles] [--removeAnnotationsFromMyOriginal] [--namespace <ns>]

If you run effluentify with no arguments, it prompts you to enter the command line interactively.

Options

Option Alias Description Default
--input Required. One or more input paths — repeat the flag for multiple (--input ./A --input ./B). A path may be a .cs file or a directory (directories are scanned recursively for .cs files).
--out Output directory. When omitted, generated code is written to the console instead of disk. console
--manyFiles Emit one configuration file per entity. When omitted, all configurations go into a single file. off
--removeAnnotationsFromMyOriginal Remove the converted annotations from the original source files. A .bak backup is written next to each modified file. off
--namespace -n Root namespace for generated files. EFFluentify.Configurations

Examples

Preview the output in the console:

effluentify --input ./Models

Generate one configuration file per entity into a folder:

effluentify --input ./Models --out ./Configurations --manyFiles

Convert and clean up the original entities (creating .bak backups), using a custom namespace:

effluentify --input ./Models --out ./Configurations --removeAnnotationsFromMyOriginal -n MyApp.Data.Configurations

Supported annotations

Property-level

Data Annotation Fluent API
[Column("name", TypeName = "...")] .HasColumnName(...) / .HasColumnType(...)
[Comment("...")] .HasComment(...)
[ConcurrencyCheck] .IsConcurrencyToken()
[DatabaseGenerated(...)] .ValueGeneratedOnAdd() / .ValueGeneratedOnAddOrUpdate() / .ValueGeneratedNever()
[DefaultValue(...)] .HasDefaultValue(...)
[MaxLength(n)] / [StringLength(n)] .HasMaxLength(n)
[Precision(...)] .HasPrecision(...)
[Required] .IsRequired()
Nullable reference / value type .IsRequired(false)
[Timestamp] .IsRowVersion()
[Unicode(...)] .IsUnicode(...)

Entity-level

Data Annotation Fluent API
[Table("name", Schema = "...")] .ToTable(...)
[Index(...)] .HasIndex(...) (with .IsUnique() / .HasDatabaseName(...))
[Key] .HasKey(...)
[Keyless] .HasNoKey()
[NotMapped] .Ignore(...)
[Comment("...")] .HasComment(...)
[ForeignKey(...)] .HasOne(...).WithMany(...).HasForeignKey(...)

Relationships are resolved across all input entities, so foreign keys and their inverse navigations are wired up together.

Project structure

The solution follows a clean-architecture layering:

Project Responsibility
EFFluentify.Domain Core models and the conversion rules (property & entity rules, rule registry). No external dependencies.
EFFluentify.Application Orchestration — the conversion service and abstractions (interfaces, pipeline options).
EFFluentify.Infrastructure Roslyn-based source parsing and annotation removal, C# code emission, and file/console I/O.
EFFluentify.Cli Entry point, argument parsing, and dependency-injection wiring. Packaged as the effluentify tool.
EFFluentify.Tests Unit and integration tests, including tests that compile the generated output.

All projects target net9.0.

Building & testing

dotnet build
dotnet test

The test suite includes integration tests that compile the generated configuration code, along with a set of expected-output fixtures under ExpectedOutput/ that lock down the emitter's behavior.

Publishing (maintainers)

The tool is published to nuget.org as EFFluentify.Tool. To cut a new release:

  1. Bump the version in EFFluentify.Cli/EFFluentify.Cli.csproj (the <Version> element).

  2. Pack:

    dotnet pack EFFluentify.Cli -c Release
    
  3. Push (dotnet nuget push is the publish step — there is no separate one):

    dotnet nuget push ./EFFluentify.Cli/bin/Release/EFFluentify.Tool.<version>.nupkg --api-key YOUR_API_KEY --source https://api.nuget.org/v3/index.json
    

Notes:

  • Get an API key from nuget.org → API Keys (scope: Push). Never commit it or share it — treat it as a secret.
  • Each version number can be pushed only once, so bump <Version> for every release.
  • Users then install or upgrade with dotnet tool install --global EFFluentify.Tool / dotnet tool update --global EFFluentify.Tool.

License

This project is licensed under the MIT License — you're free to use, modify, and distribute it, including commercially.

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.

This package has no dependencies.

Version Downloads Last Updated
1.0.2 97 9/7/2026
1.0.1 91 9/5/2026
1.0.0 89 9/5/2026