Skip to content

Releases: ReversePOCO/EntityFramework-Reverse-POCO-Code-First-Generator

v4.0.45

Choose a tag to compare

@sjh37 sjh37 released this 10 Sep 18:58

Version 4

v4 moves database reading out of the T4 template and into a separate command-line tool, efrpg. The template no longer opens a connection itself: it runs the tool, and the tool hands back the schema. That one change removes the long tail of provider-loading problems v3 had inside the Visual Studio T4 host, and it is why the upgrade needs more than a new .ttinclude.

You need both parts:

dotnet tool install -g Efrpg

plus this extension. The tool needs the .NET 10 runtime, installs once per machine, and is versioned independently of the extension. Everyone who regenerates code needs it.

Already on v3? Read Upgrading from v3 to v4. The include is versioned in its file name, so v3 and v4 sit side by side in one project and you can migrate one template at a time. The right-click Upgrade this template to v4 command makes the edits for you and shows them before writing.

What's new

Database reading

  • The efrpg dotnet tool reads the schema in its own .NET 10 process. Provider versions are the tool's problem now, not your project's: no more could not load file or assembly Npgsql / MySql.Data / System.Data.SQLite, no provider mismatches between your project and the T4 host, no 32-bit/64-bit surprises.
  • MySQL and Oracle are now fully supported 🚀
  • SQL Server, PostgreSQL and SQLite continue as before.
  • Your connection string is passed to the tool over stdin, so it never appears in process listings or command-line audit logging.
  • No Visual Studio component prerequisite. v3 needed the Entity Framework 6 tools component; v4 does not. The template no longer depends on EF6.Utility.CS.ttinclude, EnvDTE or Microsoft.VisualStudio.TextTemplating.
  • The tool and template check that they can talk to each other and say so plainly when the tool is missing or too old.

Entity Framework

  • Supports EF6 and EF Core 8, 9 and 10.
  • EF Core: table comments are written inside ToTable(...), so the generated code compiles without the HasComment obsolete warning on EF Core 10.

Visual Studio GUI - new in v4, all from the item template and the right-click menu on a .tt

  • Adding the item now asks for the database type, EF version and connection string, tests the connection with the real tool, and writes the result into the new .tt. It also derives the namespace and offers common output options: separate files, file-scoped namespaces, the unit-testing fake context, and debug-only fakes.
  • Choose tables, views and stored procedures from a checkbox tree and have the filters written into the .tt, including schema filters.
  • Settings editor for every setting, grouped as they are in Database.tt, with a search box and a link from each setting to its wiki page. Multi-line lists get a proper editor, and every change appears before it saves.
  • Enums page: see the enumerations already declared and add one from a dropdown of candidate tables, filtered as you type.
  • Callbacks page: switch each callback on or off with a tick, see its code syntax-coloured by Visual Studio, and jump straight to it in the .tt.
  • Upgrade this template to v4: converts a v3 .tt in place, deletes the settings v4 removed, and refuses a file that depends on a removed feature, saying which.

Elsewhere

  • JetBrains Rider runs the template too, without the extension: add Database.tt and EF.Reverse.POCO.v4.ttinclude to the project and run it from Rider's T4 menu. See JetBrains Rider.
  • On any host that does not supply a namespace hint, the generator derives the namespace from the .csproj.
  • Visual Studio 2026 is supported alongside 2022.
  • Values a database can hold that XML cannot, such as control characters and lone surrogates, no longer break a run.
  • The wiki has a page for every setting, and every generated-code example on it is produced by the generator and checked for drift, so the documentation cannot quietly stop being true. Start at the Settings Reference.
  • The project now lives under the ReversePOCO organisation. Old links redirect.

Deprecated and removed

Each of these stays exactly as it was in v3, which remains downloadable and continues to work, so a project that depends on one keeps using v3.

  • Visual Studio 2017 and 2019 are no longer supported. v4 needs Visual Studio 2022 or later.
  • SQL Server Compact (DatabaseType.SqlCe). It reached end of support in July 2021 and has no provider for modern .NET.
  • Custom database reader plugins (Settings.DatabaseReaderPlugin, DatabaseType.Plugin). A plugin was a .NET Framework assembly loaded into the T4 host, and there is nothing to load it into anymore. If yours reads a database we don't support, please open an issue; that is a case for adding the dialect to the tool.
  • Multiple DbContexts in one go (Settings.GenerateSingleDbContext = false, the Settings.MultiContext* settings, the MultiContext.* settings tables and the tool's --multi-context option). The v4 generator always produces one context; use the filters to shape it.
  • File-based templates (TemplateType.FileBased*, Settings.TemplateFolder). The templates live inside EF.Reverse.POCO.v4.ttinclude; edit them there and keep the edit as a patch to reapply on upgrade.
  • Settings.GeneratorType, including Custom and the GeneratorCustom class. TemplateType alone decides which generator runs.
  • JavaScript output (Settings.GenerationLanguage, Settings.FileExtension). The v4 generator only writes C# to .cs files.
  • Settings.IncludeQueryTraceOn9481Flag, the SQL Server 2014 cardinality-estimator workaround.
  • Settings.FileManagerType. Output is always written the same way. This means older C# projects' .csproj files should be upgraded to the newer SDK format to use v4; otherwise, stay on v3.
  • DatabaseReader.CleanUp(...) is now NamingHelper.CleanUp(...), since the reader moved into the tool.

Everything else - ConnectionString, TemplateType, all of FilterSettings, the naming callbacks, ElementsToGenerate, the folder settings - is unchanged. Unlike v2 to v3, this is not a settings rewrite: for most projects on SQL Server, PostgreSQL or SQLite the generated output is identical.

v3.14.1

Choose a tag to compare

@sjh37 sjh37 released this 12 Aug 15:07
  • #885 Fix SqlNullValueException on nullable SP string columns.
  • FakeDbContext security enhancements.

v3.14.0

Choose a tag to compare

@sjh37 sjh37 released this 30 May 16:33

New Features

  • SQL Server Input/Output Parameters (#868) — Support for SQL Server stored procedure input and output parameters
  • Index Include Columns (#882) — Added support for index include columns in generated indexes
  • Stored Procedure Decimal Precision (#411) — Now obtains and applies decimal precision and scale for stored procedure parameters
  • File-Scoped Namespaces (#790) — Support for file-scoped namespaces in generated code

Bug Fixes

  • #nullable Directive Handling (#883, #884) — Fixed #nullable directive bracketing for generated DbContext and factory classes
  • TableSuffix Setting — Fixed HasTrigger and MemoryOptimisedTables settings were being ignored when using TableSuffix
  • Improved handling of duplicate type cases

Note for users upgrading

There is a small breaking change for callers of generated stored procedure methods with OUTPUT parameters.

Stored-procedure OUTPUT parameters are now generated as ref instead of out (#868).
SQL Server reports every OUTPUT parameter as input/output (PARAMETER_MODE = INOUT), and such parameters can pass a value into the procedure as well as return one. Generated callers previously used out with ParameterDirection.Output, which discarded the incoming value - so procedures that read their OUTPUT parameter received null/default instead of the value supplied. They are now generated as ref with ParameterDirection.InputOutput, which sends the value in and reads the result back.

Action required: update affected call sites from out myParam to ref myParam, and initialise the variable first (a ref argument must be assigned). For example, int qty; db.MyProc(out qty); becomes int? qty = 0; db.MyProc(ref qty);. Procedures that only write to the parameter are functionally unchanged.

Why this is breaking: SQL Server's metadata (INFORMATION_SCHEMA.PARAMETERS) cannot distinguish "pure OUT" from "IN OUT" parameters - all OUTPUT parameters are reported as INOUT. The generator, therefore, treats every OUTPUT parameter as bidirectional. ref is a strict superset of out, so this is the safe default, but it does change the call site.

v3.13.0

Choose a tag to compare

@sjh37 sjh37 released this 27 Mar 20:53

This release includes many new features and bug fixes. Please update your Database.tt file with the latest version.


New Features

#690 EF Core Owned Entities (Column Grouping)

Columns with a common prefix can now be automatically grouped into an EF Core owned entity. The generator hides the individual prefixed columns from the POCO, adds a single typed navigation property instead, emits builder.OwnsOne(...) in the configuration class, and auto-generates the owned entity POCO class.

Use the new Settings.AddOwnedEntityMappings delegate:

Settings.AddOwnedEntityMappings = delegate (List<OwnedEntityMapping> ownedEntityMappings)
{
    // Map BillingAddress_Street, BillingAddress_City, etc. on Customer → Address owned entity
    ownedEntityMappings.Add(new OwnedEntityMapping
    {
        Schema       = Settings.DefaultSchema,
        Table        = "Customer",
        ColumnPrefix = "BillingAddress_",
        PropertyName = "BillingAddress",  // property name on Customer POCO
        PropertyType = "Address",         // C# type — POCO class auto-generated
    });

    // Multiple owned entities of the same type on the same table
    ownedEntityMappings.Add(new OwnedEntityMapping
    {
        Schema       = Settings.DefaultSchema,
        Table        = "Customer",
        ColumnPrefix = "ShippingAddress_",
        PropertyName = "ShippingAddress",
        PropertyType = "Address",
    });
};

The generator produces:

// Customer.cs
public Address BillingAddress { get; set; }
public Address ShippingAddress { get; set; }

// Address.cs (auto-generated owned entity POCO)
public string Street { get; set; }
public string City { get; set; }
...

// CustomerConfiguration.cs
builder.OwnsOne(x => x.BillingAddress, y =>
{
    y.Property(x => x.Street).HasColumnName("BillingAddress_Street")...;
    ...
});

Use Settings.OwnedEntityFolder to place the auto-generated owned entity POCOs in a separate sub-folder from your regular POCOs.


#854 Nullable Reverse Navigation Properties

A new setting controls whether reverse navigation properties for optional (one-to-one) relationships are generated as nullable reference types:

Settings.NullableReverseNavigationProperties = true;

When true (and AllowNullStrings = true), reverse navigation properties are emitted as MyEntity? NavigationProperty rather than MyEntity NavigationProperty. This correctly models the fact that the parent entity can exist without the child.

Note: This setting is automatically disabled for EF6 targets, as nullable reference types require C# 8 / language version 8+.


#663 [Comment] Attribute and .HasComment() Fluent Configuration

Database column and table descriptions (SQL Server extended properties, PostgreSQL COMMENT ON) are now surfaced in generated code. EF Core only.

  • [Comment("...")] data annotation added to entity classes and properties (requires Settings.UseDataAnnotations = true)
  • .HasComment("...") added to the fluent configuration for tables and columns
[Comment("The customer's primary email address")]
public string Email { get; set; }

#608 [Description] Attribute on Enum Values

When generating enumerations from database tables, enum values can now be decorated with [Description("...")] using the column's extended property description. Enable with:

Settings.AddDescriptionAttributeToEnums = true; // in the enum table settings

This makes it straightforward to display human-readable enum descriptions at runtime via reflection.


#877 More Data Annotations

When Settings.UseDataAnnotations = true, additional data annotations are now generated:

Scenario Attribute generated
Computed columns [DatabaseGenerated(DatabaseGeneratedOption.Computed)]
Identity columns with no generation [DatabaseGenerated(DatabaseGeneratedOption.None)]
Decimal columns [Precision(precision, scale)]
Non-Unicode string columns [Unicode(false)]
Required columns (non-nullable, non-computed) .IsRequired() suppressed in fluent config — handled by annotation

#829 GenerateHasDefaultValueSql Flag

EF Core only. A new flag causes .HasDefaultValueSql(...) to be emitted in the entity configuration for every column that has a SQL-level default:

Settings.GenerateHasDefaultValueSql = false; // default — set to true to enable

This is useful when you need to inspect database defaults programmatically via EF's model metadata (e.g. entity.GetProperty("CreatedAt").GetDefaultValueSql()).


#800 UseFolderNameInNamespace

When generating separate files into sub-folders, you can now automatically append the folder name to the namespace:

Settings.PocoFolder              = @"Data\Entities";
Settings.PocoConfigurationFolder = @"Data\Configuration";
Settings.UseFolderNameInNamespace = true;
// → namespace MyProject.Data.Entities
// → namespace MyProject.Data.Configuration

This eliminates the need to manually keep namespaces in sync with folder structure.


#803 Filtered Index Support via .HasFilter()

Index filter expressions (partial indexes) are now read from the database and emitted in the fluent configuration:

builder.HasIndex(x => x.Email)
       .IsUnique()
       .HasFilter("[Email] IS NOT NULL");

Supported for SQL Server, PostgreSQL, and SQLite.


#721 Stored Procedures and TVFs with Spaces in Column Names

Stored procedures and table-valued functions that return result sets with spaces in column names are now fully supported. The generated return model properties use ["Column Name"] accessor syntax where necessary, and the DbContext correctly maps them.


#859 Optional Parameters in Stored Procedures

Stored procedure parameters that have default values defined in the database are now treated as optional in the generated C# method signature. Parameters with defaults are moved to the end of the parameter list and given their database default as the C# default value, allowing callers to omit them:

// Before: all parameters required
public int MyProcAsync(int id, string name, int status)

// After: parameters with DB defaults become optional
public int MyProcAsync(int id, string name = null, int status = 0)

#873 ConnectionStringActions

EF Core only. A new setting lets you append additional method calls to the database provider setup in OnConfiguring without editing the generated context:

Settings.ConnectionStringActions = ".EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null)";

This generates:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder.UseSqlServer(connectionString + ConnectionStringActions);
}

Useful for adding retry policies, query splitting behaviour, or other provider-level options.


#807 Azure SQL Active Directory Default Authentication

The SQL Server database reader now correctly handles Authentication=Active Directory Default (and other Azure AD authentication modes) in the connection string when reverse-engineering the schema. Previously, the reader could fail to connect when using passwordless Azure AD authentication.


#609 EF Core Inheritance Workaround — Protected DbContextOptions Constructor

A protected constructor accepting the non-generic DbContextOptions is now emitted on all generated DbContext classes:

protected MyDbContext(DbContextOptions options) : base(options)
{
}

This is required when using EF Core TPH/TPT/TPC table inheritance where a derived context must call a non-generic DbContextOptions base constructor. Without it, EF Core throws a runtime error when registering the derived context.


#577 FakeDbContextInDebugOnlyMode

The Fake* unit-testing classes can now be excluded from Release builds by wrapping them in #if DEBUG:

Settings.FakeDbContextInDebugOnlyMode = false; // default — set true to wrap in #if DEBUG

Useful when you want the fake context available for development and testing but don't want it shipped in production assemblies.


#543 Stored Procedure Return Model Error Handling — Improved Defaults

The Settings.ReadStoredProcReturnObjectException delegate now stores the exception message as a comment on the generated method by default (previously it did nothing):

Settings.ReadStoredProcReturnObjectException = delegate (Exception ex, StoredProcedure sp)
{
    sp.Error = ex.Message; // ← now the default behaviour
};

If a stored procedure uses temp tables or otherwise prevents schema detection, the generated method will now contain a comment explaining why the return model is incomplete, rather than silently generating an empty return model. See the wiki page for details on how to supply the return model manually.


Bug Fixes

#876 JSON ExcludePropertyConfiguration

A new ExcludePropertyConfiguration property on JsonColumnMapping suppresses the generated builder.Property(...) fluent call for a JSON column. Use this when you are configuring the column yourself in a partial class (e.g. via OwnsMany/ToJson) to avoid the EF Core "property can only be configured once" runtime error:

jsonColumnMappings.Add(new JsonColumnMapping
{
    Schema                      = "dbo",
    Table                 ...
Read more

v3.12.0

Choose a tag to compare

@sjh37 sjh37 released this 14 Feb 16:37

This release contains a breaking change!

Please replace this line in your existing .tt files:

Settings.UpdateColumn = delegate(Column column, Table table, List<EnumDefinition> enumDefinitions)

with

Settings.UpdateColumn = delegate(Column column, Table table, List<EnumDefinition> enumDefinitions, List<JsonColumnMapping> jsonColumnMappings)

v3.11.0

Choose a tag to compare

@sjh37 sjh37 released this 03 Jan 18:26
  • #870 Added EF Core 10 support for vector and json types.
  • #855 Fix missing foreign keys when using different schemas in PostgreSQL. Thanks to Mikael Flood.
  • #864 UseMappingTables doesn't work for TemplateType.FileBasedEf6. Thanks to Simona Avornicesei.
  • #858 Enum tables PostgreSQL. Thanks to Meysam Faghfouri.
  • #562 Add DbContextOptions support for factory methods. Thanks to LarryWMSN
  • #867 Add support for partial properties. Thanks to Statler.
  • #871 NetTopologySuite is used for geography types when TemplateType.FileBasedEf6. Thanks to Simona Avornicesei.
  • Deprecate EFCore 2-6.

v3.10.0

Choose a tag to compare

@sjh37 sjh37 released this 29 Jan 15:57
  • Added EF Core 9 support
  • #849 Added support for ARM64 processor. Thanks to Rob.
  • Removed EF Core 7 (See support planning wiki)
  • Added support property for resultsets. Thanks to Frederic.
  • #821 Enable command timeout override. Thanks to Frederic.
    With this adjustment, it is possible to override the default commandtimeout at runtime which can be necessary for specific stored procedures.
_context.Database.CommandTimeout = 300;
return _context.spMyHeavyStoredProcedure.FirstOrDefault();
  • #820 Support resultset as property instead of a field in a stored procedure return model.
  • #621 Generate async methods (PR #797). Thanks to Erwin Bovendeur.
  • #819 Added "IsSynonym" property to Table class. Thanks to grantparker77.
  • #826 Allow table with periods. Periods in table names are replaced with an underscore.
  • #37 Merge duplicate stored procedure result sets. A new setting flag which defaults to true. This will cause the generator to inspect multiple result sets to see if they are all identical. If they are all identical, the duplicates will be removed, leaving a single model.
Settings.MergeMultipleStoredProcModelsIfAllSame = true;
  • #832 Add more data annotations.
[Table("table-name", Schema = "schema-name")]
  • #838 You can now generate multiple enums from a single table that contains a grouping field. Thanks to Ryan Plemons.
  • Inflector to correctly handle words and tables ending with: Status, To and Data.
  • Added more examples of adding base classes.
  • #834 SQLite - Support multiple foreign keys. Thanks to statler.
  • #298 Forward the cancellationToken parameter to the ToListAsync methods (PR #842). Thanks to mhartmair-cubido.
  • Enable more granular prepend schema support on a table and stored procedure level (PR #824). Thanks to dsfr-be and Frederic.
/// <summary>
/// Enables more granual control if the schema name should be prepend depending on the table
/// </summary>
public static Func<Table, bool> PrependSchemaNameForTable = (table) => {
    return true;
};

/// <summary>
/// Enables more granual control if the schema name should be prepend depending on the proc
/// </summary>
public static Func<StoredProcedure, bool> PrependSchemaNameForStoredProcedure = (prod) => {
    return true;
};
  • #822 Intercept stored procedure return model creation. Thanks to Frederic Desmyter.
// Enable interception of stored procedure return model when an exception occurs. Typically, when the stored procedure contains temp tables.
// This allows you render the proper error in comments or fix the return model by manually creating the ReturnModel using a list of DataColumns
public static Action<Exception, StoredProcedure> ReadStoredProcReturnObjectException = delegate (Exception ex, StoredProcedure sp)
{
    // Example
    /*if (!sp.ReturnModels.Any() && ex.Message.StartsWith("Invalid object name", StringComparison.OrdinalIgnoreCase))
    {
        if (sp.NameHumanCase.Equals("YourProcNameHere", StringComparison.OrdinalIgnoreCase))
        {
            sp.ReturnModels.Add(new List<DataColumn>
            {
                new DataColumn("Id", typeof(int)) { AllowDBNull = false, Unique = true },
                new DataColumn("Description", typeof(string))
            });
        }
    }*/
};

// Enable interception of stored procedure return model
public static Action<StoredProcedure> ReadStoredProcReturnObjectCompleted = delegate (StoredProcedure sp)
{
    // Example of how to add a row processed boolean column to a stored procedure's return model
    /*if (sp.ReturnModels.Any() && sp.NameHumanCase.Contains("process"))
    {
        var rm = sp.ReturnModels.First();
        rm.Add(new DataColumn("RowProcessed", typeof(bool)) { AllowDBNull = false });
    }*/
};

v3.9.0

Choose a tag to compare

@sjh37 sjh37 released this 19 Nov 21:39
  • #817 Added support for EFCore 8.
  • #813 Added support for remote database synonyms where tables have a trigger.
  • Deprecated EFCore 2 and 5. See Support planning for information on retirements.

v3.8.4

Choose a tag to compare

@sjh37 sjh37 released this 28 Jun 17:30
  • #807 Correct SQL for Azure stored procedures
  • #805 IDbContext not including System.Data namespace. Thanks to Greg Bartlett.
  • EF6 - Check for Spatial types and include System.Data.Entity.Spatial

v3.8.3

Choose a tag to compare

@sjh37 sjh37 released this 18 May 22:35
  • Improved support for SQLite.
  • Speeded up adding in a generator .tt settings file by not trying to immediately open a not yet defined database.