Это приложение .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, но мне не удалось его сузить ..
Шаги, которые я сделал за последние пять дней или около того ..:
- Я пытался удалить тег Обязательный из Врач и Пациент в Назначение класса.
В результате:
Невозможно определить отношение, представленное свойством навигации «Doctor.Appointments» типа «ICollection»
Удалено свойство навигации в классах «Доктор / Пациент», но проблема решена. Если я понимаю, это будет означать, что мне нужно отсканировать каждую запись в Таблице назначений и отфильтровать «Доктор равен» для определенного доктора.
Я попытался добавить новый внешний ключ в 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 и не пытайтесь создавать ссылки туда-сюда (свойства навигации все время).
Пациент имеет свойство навигации для: Назначения ->, которое имеет свойство навигации по Доктор ->, которое имеет свойство навигации по Местоположение . Это сделало навигацию более прямой, чем круг.
Заранее спасибо за помощь!