Почему EF Core создает новый теневой внешний ключ вместо использования существующего свойства?C#

Место общения программистов C#
Ответить
Anonymous
 Почему EF Core создает новый теневой внешний ключ вместо использования существующего свойства?

Сообщение Anonymous »

У меня есть два класса модели: Entity и EntityVersion (имя Entity здесь не связано с EF, это просто совпадение, учитывая домен моей компании). Там (упрощенные) модели имеют несколько отношений:
  • Одна сущность имеет множество версий EntityVersion - EntityVersion.EntityId
  • Одна сущность имеет одну (необязательно) EntityVersion - Entity.LatestEntityVersionId
  • Один набор изменений содержит много EntityVersion — EntityVersion.ChangesetId (модель набора изменений опущена для краткости — я включил эту деталь только из-за составного индекса в построителе моделей)

Код: Выделить всё

public class Entity
{
public Guid Id { get; set; }

public Guid? LatestEntityVersionId { get; set; }
}

public class EntityVersion
{
public Guid Id { get; set; }

public Guid EntityId { get; set; }
public Guid ChangesetId { get; set; }

public string? InternalName { get; set; }
public string? MigrationSource { get; set; }

// More unrelated properties
}
Построитель моделей для этих классов выглядит следующим образом:

Код: Выделить всё

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(b =>
{
b.HasOne()
.WithMany()
.HasForeignKey(x => x.LatestEntityVersionId);

b.ToTable("Entities");
});

modelBuilder.Entity(b =>
{
b.HasOne()
.WithMany()
.HasForeignKey(x => x.EntityId)
.OnDelete(DeleteBehavior.Restrict);

b.HasOne()
.WithMany()
.HasForeignKey(x => x.ChangeSetId);

b.Property(x => x.InternalName).HasColumnName("InternalName");
b.Property(x => x.MigrationSource).HasColumnName("MigrationSource");

b.HasIndex(x => x.ChangeSetId);
b.HasIndex(x => x.EntityId);
b.HasIndex(x => new { x.ChangeSetId, x.EntityId })
.IsUnique();

b.ToTable("EntityVersions");
});
}
Все работает нормально. История, над которой я работаю, заключалась в перемещении двух строк, допускающих значение NULL, из модели EntityVersion в Entity. Поскольку каждое из этих свойств использовалось примерно в 80 местах, я решил реализовать свойство навигации для облегчения доступа:

Код: Выделить всё

public class EntityVersion
{
// ...
public virtual Entity Entity { get; set; } = null!;
// ...
}
И построитель моделей:

Код: Выделить всё

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(b =>
{
//...
b.HasOne(x => x.Entity)
.WithMany()
.HasForeignKey(x => x.EntityId)
.OnDelete(DeleteBehavior.Restrict);
b.Navigation(x => x.Entity).AutoInclude();
//...
}
}
Это означало, что было легко изменить ссылки наentityVersion.InternalName наentityVersion.Entity.InternalName.
Однако, когда я опубликовал PR, создатель истории попросил меня удалить свойство EntityVersion.EntityId в пользу теневого свойства, основанного на соглашении. Нет проблем, подумал я, просто удалите свойство и укажите все ссылки наentityVersion.Entity.Id.
Однако на самом деле произошло то, что при миграции были удалены существующие внешние ключи и индексы EntityId (но остался сам столбец), добавлен новый столбец с именем EntityVersion_EntityId и добавлен внешний ключ и индексы обратно для этого нового столбца.
Вот созданный миграция:

Код: Выделить всё

public partial class EntityRelationshipsFixed : Migration
{
/// 
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_EntityVersions_Entities_EntityId",
table: "EntityVersions");

migrationBuilder.DropIndex(
name: "IX_EntityVersions_ChangeSetId_EntityId",
table: "EntityVersions");

migrationBuilder.DropIndex(
name: "IX_EntityVersions_EntityId",
table: "EntityVersions");

migrationBuilder.AddColumn(
name: "EntityVersion_EntityId",
table: "EntityVersions",
type: "uniqueidentifier",
nullable: false,
defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));

migrationBuilder.CreateIndex(
name: "IX_EntityVersions_ChangeSetId_EntityVersion_EntityId",
table: "EntityVersions",
columns: new[] { "ChangeSetId", "EntityVersion_EntityId" },
unique: true);

migrationBuilder.CreateIndex(
name: "IX_EntityVersions_EntityVersion_EntityId",
table: "EntityVersions",
column: "EntityVersion_EntityId");

migrationBuilder.AddForeignKey(
name: "FK_EntityVersions_Entities_EntityVersion_EntityId",
table: "EntityVersions",
column: "EntityVersion_EntityId",
principalTable: "Entities",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}

/// 
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_EntityVersions_Entities_EntityVersion_EntityId",
table: "EntityVersions");

migrationBuilder.DropIndex(
name: "IX_EntityVersions_ChangeSetId_EntityVersion_EntityId",
table: "EntityVersions");

migrationBuilder.DropIndex(
name: "IX_EntityVersions_EntityVersion_EntityId",
table: "EntityVersions");

migrationBuilder.DropColumn(
name: "EntityVersion_EntityId",
table: "EntityVersions");

migrationBuilder.CreateIndex(
name: "IX_EntityVersions_ChangeSetId_EntityId",
table: "EntityVersions",
columns: new[] { "ChangeSetId", "EntityId" });

migrationBuilder.CreateIndex(
name: "IX_EntityVersions_EntityId",
table: "EntityVersions",
column: "EntityId");

migrationBuilder.AddForeignKey(
name: "FK_EntityVersions_Entities_EntityId",
table: "EntityVersions",
column: "EntityId",
principalTable: "Entities",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
Это стало очевидным при запуске модульных тестов, и я продолжал получать ошибки, сообщающие, что я не могу вставить NULL в столбец EntityId, который не может иметь значение NULL. Это связано с тем, что EF Core пытается вставить Entity.Id в новое свойство EntityVersion_EntityId.
Я пытался явно определить свойство тени в построителе моделей, но это не дает результатов. разница:

Код: Выделить всё

b.HasOne(x => x.Entity)
.WithMany()
.HasForeignKey("EntityId")
.OnDelete(DeleteBehavior.NoAction);
Почему EF Core создает это новое свойство вместо использования существующего EntityId?


Подробнее здесь: https://stackoverflow.com/questions/798 ... xisting-pr
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C#»