EF Core Fluent API HasData добавление данных удаления при последующей миграции

#entity-framework-core #ef-fluent-api #ef-core-3.1

Вопрос:

Я использую HasData для заполнения данных, и он создает правильный сценарий переноса данных вставки, но при последующих миграциях он добавляет миграции удаления без каких-либо дальнейших изменений.

Сущности

Страна

 public class Country : BaseEntity
 {
     public string CountryName { get; set; }
 }
 

Округ

 public class County : BaseEntity
{
    public string CountyName { get;set; }

    public int CountryId { get;set; }

    public Country Country { get; set; }
}
 

Базовая сущность

 public class BaseEntity
{ 
    public int Id { get; set; }

    public Guid ApplicationUserId { get; set; }
}
 

Конфигурации

 public class CountyConfiguration : BaseEntityConfiguration<County>
{
    private const string TABLE_NAME = "Counties";

    public CountyConfiguration() : base(TABLE_NAME)
    {

    }

    public override void Configure(EntityTypeBuilder<County> entity)
    {
        base.Configure(entity);

        entity.Property(c => c.CountyName).IsRequired().HasMaxLength(100);
        entity.HasIndex(c => c.CountryId).IsUnique(false);
        entity.HasIndex(c => new {c.CountyName, c.CountryId }).IsUnique();
        entity.HasOne(c => c.Country).WithOne().OnDelete(DeleteBehavior.Cascade);

        entity.Ignore(c => c.ApplicationUserId);
        
        entity.HasData( 
            new County { Id = 1, CountryId = 1, CountyName = "Antrim"},
            new County { Id = 2, CountryId = 1, CountyName = "Carlow"},
            new County { Id = 3, CountryId = 1, CountyName = "Cavan"},
            new County { Id = 4, CountryId = 1, CountyName = "Clare"},
            new County { Id = 5, CountryId = 1, CountyName = "Cork"},
            new County { Id = 6, CountryId = 1, CountyName = "Derry (Londonderry)"},
            new County { Id = 7, CountryId = 1, CountyName = "Donegal"},
            new County { Id = 8, CountryId = 1, CountyName = "Dublin"},
            new County { Id = 9, CountryId = 1, CountyName = "Galway"},
            new County { Id = 10, CountryId = 1, CountyName = "Kerry"},
            new County { Id = 11, CountryId = 1, CountyName = "Kildare"},
            new County { Id = 12, CountryId = 1, CountyName = "Kilkenny"},
            new County { Id = 13, CountryId = 1, CountyName = "Laois (Queens)"},
            new County { Id = 14, CountryId = 1, CountyName = "Leitrim"},
            new County { Id = 15, CountryId = 1, CountyName = "Limerick"},
            new County { Id = 16, CountryId = 1, CountyName = "Longford"},
            new County { Id = 17, CountryId = 1, CountyName = "Louth"},
            new County { Id = 18, CountryId = 1, CountyName = "Mayo"},
            new County { Id = 19, CountryId = 1, CountyName = "Meath"},
            new County { Id = 20, CountryId = 1, CountyName = "Monaghan"},
            new County { Id = 21, CountryId = 1, CountyName = "Offaly (Kings)"},
            new County { Id = 22, CountryId = 1, CountyName = "Roscommon"},
            new County { Id = 23, CountryId = 1, CountyName = "Sligo"},
            new County { Id = 24, CountryId = 1, CountyName = "Tipperary"},
            new County { Id = 25, CountryId = 1, CountyName = "Waterford"},
            new County { Id = 26, CountryId = 1, CountyName = "Westmeath"},
            new County { Id = 27, CountryId = 1, CountyName = "Wexford"},
            new County { Id = 28, CountryId = 1, CountyName = "Wicklow"}
        );
    } 
}
 

Generated Migration1:

 public partial class County : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Counties",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                CountyName = table.Column<string>(type: "nvarchar(100)", maxLength: 100, nullable: false),
                CountryId = table.Column<int>(type: "int", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("Pk_Counties_Id", x => x.Id);
                table.ForeignKey(
                    name: "FK_Counties_Countries_CountryId",
                    column: x => x.CountryId,
                    principalTable: "Countries",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.InsertData(
            table: "Counties",
            columns: new[] { "Id", "CountryId", "CountyName" },
            values: new object[,]
            {
                { 3, 1, "Cavan" },
                { 26, 1, "Westmeath" },
                { 25, 1, "Waterford" },
                { 24, 1, "Tipperary" },
                { 23, 1, "Sligo" },
                { 22, 1, "Roscommon" },
                { 21, 1, "Offaly (Kings)" },
                { 20, 1, "Monaghan" },
                { 19, 1, "Meath" },
                { 18, 1, "Mayo" },
                { 17, 1, "Louth" },
                { 16, 1, "Longford" },
                { 27, 1, "Wexford" },
                { 15, 1, "Limerick" },
                { 13, 1, "Laois (Queens)" },
                { 12, 1, "Kilkenny" },
                { 11, 1, "Kildare" },
                { 10, 1, "Kerry" },
                { 9, 1, "Galway" },
                { 8, 1, "Dublin" },
                { 7, 1, "Donegal" },
                { 6, 1, "Derry (Londonderry)" },
                { 5, 1, "Cork" },
                { 4, 1, "Clare" },
                { 2, 1, "Carlow" },
                { 14, 1, "Leitrim" },
                { 28, 1, "Wicklow" }
            });

        migrationBuilder.CreateIndex(
            name: "IX_Counties_CountryId",
            table: "Counties",
            column: "CountryId");

        migrationBuilder.CreateIndex(
            name: "IX_Counties_CountyName_CountryId",
            table: "Counties",
            columns: new[] { "CountyName", "CountryId" },
            unique: true);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Counties");
    }
}
 

Следующая миграция 2: (Без каких-либо изменений)

 public partial class Empty : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "Counties",
            keyColumn: "Id",
            keyValue: 1);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DeleteData(
            table: "Counties",
            keyColumn: "Id",
            keyValue: 1);

        migrationBuilder.InsertData(
            table: "Counties",
            columns: new[] { "Id", "CountryId", "CountyName" },
            values: new object[] { 1, 1, "Antrim" });
    }
}
 

Не уверен, почему он добавляет migrationBuilder.DeleteData скрипт удаления?

Ответ №1:

Здесь

 entity.HasOne(c => c.Country).WithOne().OnDelete(DeleteBehavior.Cascade);
 

с WithOne() вы в основном говорите EF, что значение County.CountryId должно быть уникальным (потому что единственное различие между один к одному и один ко многим в реляционных базах данных-это уникальное ограничение (индекс) для столбцов(ов) FK).

Однако перед этим вы говорите EF обратное

 entity.HasIndex(c => c.CountryId).IsUnique(false);
 

Эта последовательность конфликтующих конфигураций каким-то образом сбивает EF с толку, и он начинает делать странные вещи.

Хотя это можно считать их ошибкой, в конце концов проблема кроется в вашем коде, поскольку, очевидно, вам не нужны отношения «один к одному». Поэтому исправьте это, заменив HasOne на HasMany

 entity.HasOne(c => c.Country).WithMany().OnDelete(DeleteBehavior.Cascade);
 

или полностью удалите его, так как все, что он делает, совпадает с основными соглашениями EF по умолчанию.

Как только вы это сделаете, вы также можете удалить HasIndex конфигурацию, поскольку она также используется по умолчанию в соответствии с соглашением об индексе EF Core FK.