Skip to content

v3.13.0

Latest

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                       = "Orders",
    Column                      = "ShippingAddress",
    PropertyType                = "Address",
    ExcludePropertyConfiguration = true  // suppress builder.Property(...) for this column
});

Additionally, AdditionalNamespace on JsonColumnMapping is now scoped to the owning table's generated file rather than added globally to all files.

#706 Timestamp / RowVersion Column Length

Timestamp and rowversion columns were incorrectly generating a .HasMaxLength(8) call, which is invalid in SQL Server and caused runtime errors. These columns no longer emit a length specification.

#681 Nullable Geography Type in Stored Procedure

Nullable geography parameter types in stored procedure return models were not being handled correctly for EF6, causing a generation error. Fixed.

#675 Extended Properties Null When UpdateColumn Delegate Called

A NullReferenceException could occur when Settings.UpdateColumn was invoked for columns that had no extended properties. Extended properties are now safely null-checked before access.

#580 Column/Table Names with Trailing Whitespace

Column and table names retrieved from the database that had trailing whitespace were not being trimmed, causing schema-reading failures and incorrect name generation. All names are now trimmed at read time.

#878 Nullable Reverse Navigation

Nullable reference types require C# 8+, which EF6 projects do not use. The setting is automatically forced to false for TemplateType.Ef6 and TemplateType.FileBasedEf6.


New Settings Summary

Setting Type Default Notes
NullableReverseNavigationProperties bool false Nullable one-to-one reverse nav props (EF Core only)
GenerateHasDefaultValueSql bool false Emit .HasDefaultValueSql() in config (EF Core only)
UseFolderNameInNamespace bool false Append folder name to namespace
FakeDbContextInDebugOnlyMode bool false Wrap Fake* classes in #if DEBUG
ConnectionStringActions string "" Extra method chain on provider setup (EF Core only)
OwnedEntityFolder string "" Sub-folder for auto-generated owned entity POCOs
AddOwnedEntityMappings delegate Configure owned entity column groupings