Код первый: EntityFramework - ошибка миграции в отношении «один ко многим» - PullRequest
0 голосов
/ 10 июля 2019

Это приложение .NET Core 2.2. С EF Core. Код первый. Я немного младше экспериментирую с более тяжелым проектом для собственного обучения.

Для упрощения структуры модели см .: Модель-структура . Обычно существует абстрактный пользователь по умолчанию, который наследуется врачом и пациентом.

Оба Доктор и Пациент имеют много назначений . Доктор со многими Пациенты , Пациент со многими Врачи . (или с тем же Пат / Док). Ссылка на Назначения в обоих классах выглядит так:

public virtual ICollection<Appointment> Appointments { get; set; }

В Назначении Обе Врач и Пациент Требуются. Поскольку ни одно назначение не ожидается без какого-либо из них.

[Required]
    public Doctor Doctor { get; set; }

[Required]
    public Patient Patient { get; set; }

Когда они совпадают на определенной встрече, создаются время, место и т. Д. Тем не менее, Назначение должно содержать справку для Доктора и Пациента .

Когда удаляется определенный пользователь, мне нужно удалить и соответствующие встречи. Но не связанный другой пользователь, местоположение и т. Д.

Add-Migration создан. Ошибка возникает, когда я хочу обновить базу данных.

Часть файла миграции. Что странно, что DoctorID добавляется как FK один раз с ограничением и один раз с каскадом. Вопрос 1) Не уверен, верно ли это и могу ли я коснуться его напрямую?

migrationBuilder.CreateTable(
name: "Appointment",
columns: table => new
{
    Id = table.Column<Guid>(nullable: false),
    LocationId = table.Column<Guid>(nullable: false),
    CreatedDate = table.Column<DateTime>(nullable: false),
    StartTime = table.Column<DateTime>(nullable: false),
    Duration = table.Column<TimeSpan>(nullable: false),
    FollowUpAppointmentId = table.Column<Guid>(nullable: true),
    DoctorId = table.Column<Guid>(nullable: true)
},
constraints: table =>
{
    table.PrimaryKey("PK_Appointment", x => x.Id);
    table.ForeignKey(
        name: "FK_Appointment_Doctors_DoctorId",
        column: x => x.DoctorId,
        principalTable: "Doctors",
        principalColumn: "Id",
        onDelete: ReferentialAction.Restrict);
    table.ForeignKey(
        name: "FK_Appointment_Doctors_Id",
        column: x => x.Id,
        principalTable: "Doctors",
        principalColumn: "Id",
        onDelete: ReferentialAction.Cascade);
    table.ForeignKey(
        name: "FK_Appointment_Patients_Id",
        column: x => x.Id,
        principalTable: "Patients",
        principalColumn: "Id",
        onDelete: ReferentialAction.Cascade);
}};

В ApplicationDbContext я использовал Fluent API для настройки отношений.

builder.Entity<Doctor>()
    .HasMany<Appointment>(ap => ap.Appointments)
    .WithOne(d => d.Doctor)
    .HasForeignKey(fk => fk.Id)
    .OnDelete(DeleteBehavior.Cascade);

builder.Entity<Patient>()
    .HasMany<Appointment>(ap => ap.Appointments)
    .WithOne(p => p.Patient)
    .HasForeignKey(fk => fk.Id)
    .OnDelete(DeleteBehavior.Cascade);

Сообщение об ошибке относится к проблеме с циклическими ссылками, но я не вижу, чтобы это было честно - если только в случае, т. Е. Доктор удален -> запускает удаление Назначения -> вызывает удаление пациента (который это явно не намерение)

Ошибка: введение ограничения FOREIGN KEY 'FK_Appointment_Patients_Id' В таблице «Назначение» могут возникнуть циклы или несколько каскадных путей. Укажите ON DELETE NO ACTION или ON UPDATE NO ACTION или измените другое Ограничения FOREIGN KEY.

Я не уверен, является ли это проблемой дизайна или проблемой кодирования.

Я пытался получить некоторую помощь от MS Docs , от EFCore.com , просмотрел несколько сообщений SO, но мне не удалось его сузить ..

Шаги, которые я сделал за последние пять дней или около того ..:

  1. Я пытался удалить тег Обязательный из Врач и Пациент в Назначение класса. В результате:

Невозможно определить отношение, представленное свойством навигации «Doctor.Appointments» типа «ICollection»

  1. Удалено свойство навигации в классах «Доктор / Пациент», но проблема решена. Если я понимаю, это будет означать, что мне нужно отсканировать каждую запись в Таблице назначений и отфильтровать «Доктор равен» для определенного доктора.

  2. Я попытался добавить новый внешний ключ в Appointment class с DoctorId и PatientId, но похоже, что это выполняется автоматически при миграции (в файле миграции ключ FK создается соответственно ). В любом случае это привело к той же ошибке.

    public Guid DoctorId {get; задавать; } public Guid PatientId {get; задавать; }

Вопрос 2 : Может быть, он глуп, но я понимаю, что у Встречи есть отношения один-ко-многим с Доктором и Пациентом, поэтому не уверен, что класс Join Table поможет? Но в таком случае, как работает каскадное удаление?

т.е.: я создаю класс ComboDocApp , который имеет ссылку для одного Доктора и одного Назначения . И Доктор имеет ICollection, а Назначение также имеет ICollection. Тем не менее, это кажется странным, поскольку для назначения требуется только один ComboDocApp .

EDIT: Итак, после комментариев я создал новый класс ComboDocPatAppLoc.

    public class ComboDocPatAppLoc
{
    [Key]
    public Guid Id { get; set; }

    [Required]
    public Doctor Doctor { get; set; }
    public Guid DoctorId { get; set; }

    [Required]
    public Patient Patient { get; set; }
    public Guid PatientId { get; set; }

    [Required]
    public Appointment Appointment { get; set; }
    public Guid AppointmentId { get; set; }

    [Required]
    public Location Location { get; set; }
    public Guid LocationId { get; set; }
}

В ApplicationDbContext я создал отношения следующим образом:

builder.Entity<ComboDocPatAppLoc>()
    .HasKey(key => new { key.DoctorId, key.PatientId, key.AppointmentId, key.LocationId });

builder.Entity<ComboDocPatAppLoc>()
    .HasOne(d => d.Doctor)
    .WithMany(cdpa => cdpa.ComboDocPatAppLoc)
    .HasForeignKey(fk => fk.Id);

builder.Entity<ComboDocPatAppLoc>()
    .HasOne(p => p.Patient)
    .WithMany(cdpa => cdpa.ComboDocPatAppLoc)
    .HasForeignKey(fk => fk.Id);

builder.Entity<ComboDocPatAppLoc>()
    .HasOne(l => l.Location);

builder.Entity<ComboDocPatAppLoc>()
    .HasOne(app => app.Appointment)
    .WithOne(cdpa => cdpa.ComboDocPatAppLoc);

И все же ошибка остается:

Введение ограничения FOREIGN KEY 'FK_ComboDocPatAppLoc_Patients_Id' в таблицу 'ComboDocPatAppLoc' может вызывать циклы или несколько каскадных путей

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

  • Если я удалю Доктора / Пациента -> Мне нужно иметь Назначение и ComboDocPatAppLoc удалено.
  • Если я удалю Назначение -> Мне нужно иметь ComboDocPatAppLoc удалено.
  • Если я удалю Местоположение -> Мне нужно иметь Назначение
    удален.

UDPATE :

Хорошо, в конце концов я обнаружил, что это скорее проблема дизайна, и переделка помогла решить ее. Доктор и Пациент классы имели ссылку на Назначения и Назначения имели ссылку на Врачи и Пациенты .

Почему это было проблемой : В случае удаления Пациента каскадное удаление приведет к удалению Встречи со всеми ссылками на Местоположение доктора и т. Д. Так как у Доктора был также ссылка на местоположение, определенная запись о местоположении была бы удалена дважды каскадной последовательностью.

Решение : Положитесь на Discovery и не пытайтесь создавать ссылки туда-сюда (свойства навигации все время).

Пациент имеет свойство навигации для: Назначения ->, которое имеет свойство навигации по Доктор ->, которое имеет свойство навигации по Местоположение . Это сделало навигацию более прямой, чем круг.

Заранее спасибо за помощь!

Ответы [ 2 ]

0 голосов
/ 11 июля 2019

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

Salute

0 голосов
/ 10 июля 2019

Я уверен, что единственный способ заставить это работать с вашими текущими настройками, это изменить следующую строку:

.OnDelete(DeleteBehavior.Cascade);

до

.OnDelete(DeleteBehavior.SetNull);

...