#asp.net-core #entity-framework-core #foreign-keys
#asp.net-core #entity-framework-core #внешние ключи
Вопрос:
В настоящее время у меня есть рабочее отношение «один ко многим» между сущностями «Диалог» и «Сообщение», где в разговоре может быть несколько сообщений.
Это работает нормально:
public class Conversation
{
public long ID { get; set; }
}
public class Message : IEntity
{
public virtual Conversation Conversation { get; set; }
public long ConversationID { get; set; }
public long ID { get; set; }
}
Тем не менее, я пытаюсь добавить свойство навигации к классу ‘Conversation’ с именем ‘LastMessage’, которое будет отслеживать последнюю созданную запись сообщения:
public class Conversation
{
public long ID { get; set; }
public virtual Message LastMessage { get; set; }
public long LastMessageID { get; set; }
}
Когда я пытаюсь применить вышеуказанное, я получаю сообщение об ошибке
Система.Исключение InvalidOperationException: дочерняя / зависимая сторона не может быть определена для отношения «один к одному» между диалогом.Последнее сообщение ‘ и ‘Сообщение.Беседа ‘.
Как мне поддерживать связь «один ко многим» между «Разговором» и «Сообщением», а ТАКЖЕ добавлять свойство навигации в классе «Conversation», которое переходит к одной записи «Message»?
Ответ №1:
Если в диалоге может быть несколько сообщений, это называется отношениями «один ко многим». Вы должны исправить таблицы:
public class Conversation
{
[Key]
public long ID { get; set; }
[InverseProperty(nameof(Message.Conversation))]
public virtual ICollection<Message> Messages { get; set; }
}
public class Message
{
[Key]
public long ID { get; set; }
public long ConversationID { get; set; }
[ForeignKey(nameof(ConversionId))]
[InverseProperty("Messages")]
public virtual Conversation Conversation { get; set; }
}
Ответ №2:
После того, как я попробовал всевозможные аннотации к данным и беглый API, самое чистое решение, которое я мог придумать, оказалось очень простым, которое не требует ни того, ни другого. Для этого требуется только добавить «частный» конструктор в класс Conversation (или «защищенный», если вы используете отложенную загрузку), в который вводится ваш объект DbContext. Просто настройте свои классы ‘Conversation’ и ‘Message’ как обычное отношение «один ко многим», и теперь, когда контекст вашей базы данных доступен из объекта ‘Conversation’, вы можете заставить ‘LastMessage’ просто возвращать запрос из базы данных с помощью метода Find (). Метод Find() также использует кэширование, поэтому, если вы вызываете средство получения более одного раза, оно совершит только один переход к базе данных.
Вот документация по этой возможности: https://docs.microsoft.com/en-us/ef/core/modeling/constructors#injecting-services
Примечание: свойство ‘LastMessage’ доступно только для чтения. Чтобы изменить его, установите свойство ‘LastMessageID’.
class Conversation
{
public Conversation() { }
private MyDbContext Context { get; set; }
// make the following constructor 'protected' if you're using Lazy Loading
// if not, make it 'private'
protected Conversation(MyDbContext Context) { this.Context = Context; }
public int ID { get; set; }
public int LastMessageID { get; set; }
public Message LastMessage { get { return Context.Messages.Find(LastMessageID); } }
}
class Message
{
public int ID { get; set; }
public int ConversationID { get; set; }
public virtual Conversation Conversation { get; set; }
}