dotnet/efcore

Setting entity/property name via IEntityTypeAddedConvention breaks migration

grosch-intl opened this issue · 3 comments

Ask a question

When I run an initial migration, everything works without issue, and the many-to-many table relationship you auto-generated below appears in the migration. However, if I try to use an IEntityTypeAddedConvention to make things snake case, the migration aborts.

Include your code

internal partial class RandomEntityTypeConvention : IEntityTypeAddedConvention
{
    private static readonly Regex UpperCaseRegex = AutoGeneratedRegex();

    [GeneratedRegex("([a-z0-9])([A-Z])", RegexOptions.Compiled | RegexOptions.ExplicitCapture, matchTimeoutMilliseconds: 1000)]
    private static partial Regex AutoGeneratedRegex();

    private static string ToSnakeCase(string name) => UpperCaseRegex.Replace(name, "$1_$2").ToLowerInvariant();

    #region IEntityTypeAddedConvention implementation
    public void ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext<IConventionEntityTypeBuilder> context) {
        var entityType = entityTypeBuilder.Metadata;

        var name = entityType.ClrType.Name;
        entityType.SetTableName(ToSnakeCase(name));

        foreach (var property in entityType.GetProperties())
            property.SetColumnName(ToSnakeCase(property.Name));
    }
    #endregion
}

Failure message from migration

Unable to create a 'DbContext' of type 'SdcDatabaseContext'. The exception 'Cannot use table 'dictionary2' for entity type 'ExportControlClassificationNumberSoftwarePackage (Dictionary<string, object>)' since it is being used for entity type 'DistributionFileSoftwarePackage (Dictionary<string, object>)' and potentially other entity types, but there is no linking relationship. Add a foreign key to 'ExportControlClassificationNumberSoftwarePackage (Dictionary<string, object>)' on the primary key properties and pointing to the primary key on another entity type mapped to 'dictionary2'.' was thrown while attempting to create an instance. For the different patterns supported at design time, see https://go.microsoft.com/fwlink/?linkid=851728

Good code you generate if I don't add the convention

migrationBuilder.CreateTable(
    name: "ExportControlClassificationNumberSoftwarePackage",
    columns: table => new
    {
        ExportControlClassificationNumbersId = table.Column<int>(type: "integer", nullable: false),
        SoftwarePackagesId = table.Column<int>(type: "integer", nullable: false)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_ExportControlClassificationNumberSoftwarePackage", x => new { x.ExportControlClassificationNumbersId, x.SoftwarePackagesId });
        table.ForeignKey(
            name: "FK_ExportControlClassificationNumberSoftwarePackage_ExportCont~",
            column: x => x.ExportControlClassificationNumbersId,
            principalTable: "ExportControlClassificationNumber",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
        table.ForeignKey(
            name: "FK_ExportControlClassificationNumberSoftwarePackage_SoftwarePa~",
            column: x => x.SoftwarePackagesId,
            principalTable: "SoftwarePackage",
            principalColumn: "Id",
            onDelete: ReferentialAction.Cascade);
    });

Include provider and version information

EF Core version: 9.0.0
Database provider: Npgsql.EntityFrameworkCore.PostgreSQL 9.0.1
Target framework: .NET 9
Operating system: Windows 11
IDE: Visual Studio 2022 17.12.3

If I instead do this via OnModelCreating by looping through modelBuilder.Model.GetEntityTypes() then it doesn't fail.