Вся ошибка:
Свойство внешнего ключа «Appointment.CustomerId1» было создано в теневом состоянии, поскольку в типе объекта существует конфликтующее свойство с простым именем «CustomerId», но оно либо не сопоставлено, либо уже используется для другого отношения, либо несовместимо с соответствующий тип первичного ключа.
Таблицы:
В таблице назначений у меня было 2 FK: один указывал на таблицу клиентов (столбец идентификаторов), а другой - на таблицу сотрудников (столбец идентификаторов). Смотрите картинку в разделе ДО. Но теперь, поскольку у меня будут все пользователи (клиент + сотрудник) в одной таблице User, это должно измениться. Это означает, что в таблице назначений мне нужно иметь 2 FK, но оба должны указывать на одного и того же пользователя таблицы и столбец идентификатора. Я хочу, чтобы CustomerId и EmployeeId указывали на идентификатор пользователя.
Он создает 3 дополнительных столбца: UserId, CustomerId1 и EmployeeId1, которые мне не нужны. Я использовал только соглашения для отношений, никаких аннотаций данных или Fluent API.
Есть 3 вещи, которые могут вызвать ошибку:
Насколько я понимаю
Двунаправленный метод:
Класс пользователя:
public ICollection<Appointment> AppointmentCustomers { get; set; }
public ICollection<Appointment> AppointmentEmployees { get; set; }
Класс назначения:
public string CustomerId { get; set; }
[ForeignKey("CustomerId")]
public User Customer { get; set; }
public string EmployeeId { get; set; }
[ForeignKey("EmployeeId")]
public User Employee { get; set; }
Метод OnModelCreating:
builder.Entity<Appointment>()
.HasOne(u => u.Customer)
.WithMany(app => app.AppointmentCustomers)
.HasForeignKey(u => u.CustomerId)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<Appointment>()
.HasOne(u => u.Employee)
.WithMany(app => app.AppointmentEmployees)
.HasForeignKey(u => u.EmployeeId)
.OnDelete(DeleteBehavior.NoAction);
Что такое (1 конкретный исследованный не повторяющийся) вопрос? Как спросить Справочный центр
Пожалуйста, не вставляйте «EDIT»/«UPDATE», просто сделайте свой пост лучшей презентацией на момент редактирования. Пожалуйста, не повторяйте код в прозе. Пожалуйста, не включайте текст в изображения и не добавляйте лишние изображения.
Вопросы отладки требуют минимального воспроизводимого примера — вырезание, вставка и исполняемый код, включая инициализацию; желаемый и фактический вывод (включая дословные сообщения об ошибках); теги и версии; четкая спецификация и объяснение. Для SQL включите DDL и табличный код инициализации. Для отладки, которая включает в себя наименьшее количество кода, который вы можете указать, это код, который вы показываете в порядке, расширенный кодом, который вы показываете, не в порядке. Как спрашивать Справочный центр Когда вы получаете результат, которого не ожидаете, приостановите общую цель, перейдите к 1-му подвыражению с неожиданным результатом и скажите, что вы ожидали и почему, подкрепленное документацией. (Основы отладки.)
Раньше EF мог работать с ключами по соглашению. Соглашение основано на имени типа (сотрудник или клиент), а не на имени переменной. Если бы вы назвали FKs EmployeeKey/EmpID и CustomerKey/CustID, у вас возникла бы аналогичная проблема. Соглашение EF не связало бы их с FK, поэтому оно создало бы теневые свойства.
Теперь, когда вы указываете их обоих на один и тот же тип (пользователь), EF не может использовать соглашение, поэтому вы должны быть явными. Самый простой способ исправить это — использовать атрибут [ForeignKey]
. Это можно либо поместить в свойство FK, чтобы указать на свойство навигации, либо в свойство навигации, чтобы указать на FK.
т.е.
[ForeignKey(nameof(Customer))]
public string CustomerId { get; set; }
[ForeignKey(nameof(Employee))]
public string EmployeeId { get; set; }
public virtual User Customer { get; set; }
public virtual User Employee { get; set; }
или
public string CustomerId { get; set; }
public string EmployeeId { get; set; }
[ForeignKey(nameof(CustomerId))]
public virtual User Customer { get; set; }
[ForeignKey(nameof(EmployeeId))]
public virtual User Employee { get; set; }
Редактировать:
Ошибка, которую вы сейчас получаете, связана с тем, что у пользователя есть коллекция встреч, но вам нужно указать EF, на какую коллекцию ссылается этот FK. Здесь у вас есть встреча, которая ссылается на двух пользователей, клиента и сотрудника. С точки зрения пользователя существует 2 различных отношения. Назначения-Я-Клиент и Назначения-Я-Сотрудник.
Если вас интересует только одно отношение, вам необходимо настроить EF для сопоставления User.Appointments на основе отношения «Назначение клиента» или «Сотрудник». Если вы заинтересованы в обоих, то у вас есть два варианта.
a) Добавьте 2 коллекции Appointments в User, т. е. AppointmentsAsCustomer и AppointmentsAsEmployee, и настройте сопоставления в ModelBuilder или EntityTypeConfiguration.
б) Удалите коллекцию «Назначения» для пользователя и настройте EF с помощью .HasOne(x => x.Customer).WithMany()
и .HasOne(x => x.Employee).WithMany()
, а затем, когда вы хотите запросить встречи пользователя, сделайте это из «Встречи», а не ожидайте перехода через пользователя:
вместо того:
var appointmentsForUser = context.Users
.Where(u => u.UserId == userId)
.SelectMany(u => u.Appointments) // can't work for both asEmployee and asCustomer
.ToList();
использовать:
var appointmentsForUser = context.Appointments
.Where(a => a.Customer.UserId == userId
|| a.Employee.UserId == userId) // if you want appts where user is customer or employee
.ToList();
Двунаправленные ссылки (где Apointments ссылается на пользователей, а User ссылается на Appointments) следует использовать с осторожностью, только в случае крайней необходимости, а не по умолчанию. Они могут привести к странному поведению при обновлении, а также последствиям ленивой загрузки и подобным «подводным камням» с неоднозначными отношениями.
Здравствуйте и спасибо за быстрый и четкий ответ. Теперь я понимаю свою ошибку, имя важно. Однако я сделал то, что вы мне сказали, и попробовал оба ваших примера (даже если они одинаковы), и теперь, когда я пытаюсь добавить миграцию, у меня появляется эта новая ошибка: Невозможно определить отношение, представленное навигацией «Назначение. Клиент» типа «Пользователь». Либо вручную настройте связь, либо игнорируйте это свойство с помощью атрибута [NotMapped] или с помощью EntityTypeBuilder.Ignore в OnModelCreating. Я попытаюсь решить эту проблему, но вы хоть представляете, что это такое?
Я расширил ответ объяснением ошибки, которую вы теперь получаете, поскольку я пропустил, что у вас есть ссылка на коллекцию для назначений, объявленных в таблице пользователей, что вызовет проблемы.
Привет, и извините за позднее обновление. а) Я добавил 2 новые коллекции в классе User: i.stack.imgur.com/o7t5Y.png Я настроил их с помощью FluentAPI в OnModelCreating with ModelBuilder: i.stack.imgur.com/MOgfr.png Я также изменил класс Appointment: i.stack.imgur.com/v2nTN.png b) Я удалил 2 новые коллекции Appointment из User. Я настроил их с помощью FluentAPI: i.stack.imgur.com/qRAxP.png И все. Что я делаю неправильно? Оба метода имеют одинаковую проблему (теневые свойства). Есть ли что-то, что EF перезаписывает...? Спасибо.
Похоже, вы не сделали то, что я указал в ответе. Когда у вас есть 2 или более ссылок из одной сущности на одну и ту же сущность (клиенты и сотрудники имеют тип пользователя), вы должны сообщить EF, какой внешний ключ использовать для каждой из них. Соглашение EF автоматически определяет FK, но делает это по имени типа, а не по имени свойства. Поэтому, если тип «Пользователь», он ожидает «UserId», вы должны указать ему использовать «CustomerId». Это можно сделать с помощью атрибута [ForeignKey]
или с помощью .HasForeignKey()
в ModelBuilder.
Здравствуйте. Я так и сделал, если вы посмотрите на мою вторую картинку (i.stack.imgur.com/MOgfr.png). Существует .HasForeignKey(), и теперь я также использовал аннотацию данных: [ForeignKey(nameof(Customer))] для свойства CustomerId, и это все еще не работает. Я очень внимательно прочитал и следил за вашими комментариями и прочитал больше данных об этом, и я стараюсь изо всех сил, но я не могу понять, что я делаю неправильно с такой простой задачей.
То, что вы сделали, сбивает с толку, потому что последняя ссылка FluentAPI не имеет регистрации FK. Не видя реального кода, будет сложно увидеть полное состояние конфигурации и выявить проблемы. Это невозможно сделать с выборочными скриншотами. Рассмотрите возможность выделения только тех классов, свойств и конфигураций в отдельный проект, который воспроизводит проблему, и загрузите его на Github или вставьте код в вопрос. При решении проблем это помогает разбить все на минимальный воспроизводимый пример для работы.
Я понимаю, ок. Я отредактировал свой вопрос, вы можете видеть это в разделе «Двунаправленный метод». Надеюсь, теперь мой код понятен и легко читается.
Свойство уже использовалось для другого отношения из старых классов Customer и Employee.
(Ответ Стива Пи очень помогает с настройками внешнего ключа, но из того, что я читал, лучше настраивать их с помощью Fluent API и аннотаций данных.)