efcore / EFCore.NamingConventions

Entity Framework Core plugin to apply naming conventions to table and column names (e.g. snake_case)
Apache License 2.0
738 stars 74 forks source link

UseSnakeCaseNamingConvention() creates unnecessary foreign key for owned type when using TPT mapping strategy #191

Open Gbsyi opened 1 year ago

Gbsyi commented 1 year ago

When I create an own-type class and try to configure it using OwnsOne(), ef creates an unnecessary foreign key to the parent table. If I remove UseSnakeCaseNamingConvention(), there is no foreign key for own type.

How to reproduce

Link to my repo with bug: https://github.com/Gbsyi/EfTpt There are some snippets from that repo.

Models:

public abstract class Transport
{
    public Guid Id { get; set; }

    protected Transport()
    {
    }
}

public class Speed
{
    public int Value { get; }
}

public class Car : Transport
{
    public Speed Speed { get; set; }
}

Configuration:

modelBuilder.Entity<Transport>(e =>
        {
            e.HasKey(x => x.Id);
            e.UseTptMappingStrategy();
        });

        modelBuilder.Entity<Car>(e =>
        {
            e.OwnsOne(x => x.Speed, bld =>
            {
                bld.Property(x => x.Value)
                    .HasColumnName("speed");
            });
        });

What it creates:

Migration:

migrationBuilder.CreateTable(
                name: "Cars",
                columns: table => new
                {
                    id = table.Column<Guid>(type: "uuid", nullable: false),
                    speed = table.Column<int>(type: "integer", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Cars", x => x.id);
                    table.ForeignKey(
                        name: "fk_cars_cars_id",
                        column: x => x.id,
                        principalTable: "Transports",
                        principalColumn: "id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "fk_cars_transports_id",
                        column: x => x.id,
                        principalTable: "Transports",
                        principalColumn: "id",
                        onDelete: ReferentialAction.Cascade);
                });

Migration.Designer:

modelBuilder.Entity("EfTpt.Ef.Models.Car", b =>
                {
                    b.HasOne("EfTpt.Ef.Models.Transport", null)
                        .WithOne()
                        .HasForeignKey("EfTpt.Ef.Models.Car", "Id")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired()
                        .HasConstraintName("fk_cars_transports_id");

                    b.OwnsOne("EfTpt.Ef.Types.Speed", "Speed", b1 =>
                        {
                            b1.Property<Guid>("CarId")
                                .HasColumnType("uuid")
                                .HasColumnName("id");

                            b1.Property<int>("Value")
                                .HasColumnType("integer")
                                .HasColumnName("speed");

                            b1.HasKey("CarId");

                            b1.ToTable("Cars");

                            b1.WithOwner()
                                .HasForeignKey("CarId")
                                .HasConstraintName("fk_cars_cars_id");
                        });

                    b.Navigation("Speed")
                        .IsRequired();
                });

What it will create if I'll remove UseSnakeCaseNamingConvention()

Migration:

migrationBuilder.CreateTable(
                name: "Cars",
                columns: table => new
                {
                    Id = table.Column<Guid>(type: "uuid", nullable: false),
                    speed = table.Column<int>(type: "integer", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Cars", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Cars_Transports_Id",
                        column: x => x.Id,
                        principalTable: "Transports",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                });

Migration.Designer:

modelBuilder.Entity("EfTpt.Ef.Models.Car", b =>
                {
                    b.HasOne("EfTpt.Ef.Models.Transport", null)
                        .WithOne()
                        .HasForeignKey("EfTpt.Ef.Models.Car", "Id")
                        .OnDelete(DeleteBehavior.Cascade)
                        .IsRequired();

                    b.OwnsOne("EfTpt.Ef.Types.Speed", "Speed", b1 =>
                        {
                            b1.Property<Guid>("CarId")
                                .HasColumnType("uuid");

                            b1.Property<int>("Value")
                                .HasColumnType("integer")
                                .HasColumnName("speed");

                            b1.HasKey("CarId");

                            b1.ToTable("Cars");

                            b1.WithOwner()
                                .HasForeignKey("CarId");
                        });

                    b.Navigation("Speed")
                        .IsRequired();
                });

Provider and version information

EF Core version: 7.0.3 Database provider: Npgsql.EntityFrameworkCore.PostgreSQL 7.0.3 Target framework: NET 7.0 EFCore.NamingConventions version: 7.0.2