Я занимаюсь проектированием инфраструктуры сущностей с кодом.
У меня есть таблица Account, в которой есть свойство Supervisor:
public class Account
{
public int Id { get; set; }
public Account Supervisor { get; set; }
}
Это прекрасно работает.
Однако я хочу добавить в класс альтернативного руководителя:
public class Account
{
public int Id { get; set; }
public Account Supervisor { get; set; }
public Account AlternateSupervisor { get; set; }
}
Когда я запускаю Add-Migration AddAlternateSupervisor, сгенерированный код дает мне следующее:
public partial class AddAlternateSupervisor : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_Accounts_Accounts_SupervisorId",
table: "Accounts");
migrationBuilder.DropIndex(
name: "IX_Accounts_SupervisorId",
table: "Accounts");
migrationBuilder.AddColumn<int>(
name: "AlternateSupervisorId",
table: "Accounts",
nullable: true);
migrationBuilder.CreateIndex(
name: "IX_Accounts_AlternateSupervisorId",
table: "Accounts",
column: "AlternateSupervisorId",
unique: true,
filter: "[AlternateSupervisorId] IS NOT NULL");
migrationBuilder.AddForeignKey(
name: "FK_Accounts_Accounts_AlternateSupervisorId",
table: "Accounts",
column: "AlternateSupervisorId",
principalTable: "Accounts",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
}
// snip
}
Как видите, EF пытается переименовать мою ссылку с Supervisor на AlternateSupervisor. Я не хочу этого, я хочу, чтобы и Supervisor, и AlternateSupervisor ссылались на другие учетные записи.
Я знаю, что EF не может обрабатывать несколько отношений «многие ко многим», но это отношения «один к одному». Кажется, я не могу найти никакой информации о том, почему EF генерирует такую миграцию.
Итак, почему Entity Framework пытается переименовать Supervisor в AlternateSupervisor и как я могу заставить его генерировать обе ссылки?
Обновлено: на этот вопрос был дан ответ, заданный изначально. Тем не менее, я хотел бы добавить, что заданный вопрос на самом деле не имеет большого смысла в предметной области. Кто когда-либо слышал об учетной записи, которая могла контролировать ровно одну другую учетную запись? Отношение представляет собой отношение «один ко многим», которое разрешается путем использования WithMany вместо WithOne.





EF Core не может сопоставлять несколько one-to-one с одним и тем же объектом по соглашению. Вы должны сделать это с Fluent API следующим образом:
Ваш Account класс:
public class Account
{
public int Id { get; set; }
public int SupervisorId { get; set; }
public Account Supervisor { get; set; }
public int AlternateSupervisorId { get; set; }
public Account AlternateSupervisor { get; set; }
}
Затем в OnModelCreating из DbContext следующим образом:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Account>().HasOne(a => a.Supervisor).WithOne()
.HasForeignKey<Account>(a => a.SupervisorId).OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Account>().HasOne(a => a.AlternateSupervisor).WithOne()
.HasForeignKey<Account>(a => a.AlternateSupervisorId).OnDelete(DeleteBehavior.Restrict);
}
Теперь все будет генерироваться, как и ожидалось!
Примечание. Я добавил внешний ключ SupervisorId и AlternateSupervisorId явно в класс модели Account для удобочитаемости. Если вы не хотите этого явно, тогда конфигурация Fluent API должна быть следующей:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Account>().HasOne(a => a.Supervisor)
.WithOne().OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Account>().HasOne(a => a.AlternateSupervisor)
.WithOne().OnDelete(DeleteBehavior.Restrict);
}
Да! Ты прав. Потому что без этого нельзя вставить исходные данные. :)
На самом деле я понял, что в любом случае мне нужно что-то немного другое, поскольку на самом деле я не хочу отношений один к одному, поскольку один супервайзер может контролировать множество учетных записей, но вы ответили на мой вопрос именно так, как он был задан, так что слава!
Спасибо. Это работает. Я не уверен, что на 100% понимаю, почему это необходимо или почему это работает. В классе Account нужно пометить int как целое, допускающее значение NULL, если супервизор или альтернативный супервизор не является обязательным.