代码之家  ›  专栏  ›  技术社区  ›  user3163495

实体框架核心-一对多,但父级也有一个子级的导航属性?

  •  0
  • user3163495  · 技术社区  · 5 年前

    我目前在实体“Conversation”和“Message”之间有一个有效的一对多关系,其中一个会话可以有多个消息。

    这很好:

    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; }
    }
    

    但是,我正在尝试向名为“LastMessage”的“Conversation”类添加导航属性,该类将跟踪创建的最后一条消息记录:

    public class Conversation
    {
        public long ID { get; set; }
        public virtual Message LastMessage { get; set; }
        public long LastMessageID { get; set; }
    }
    

    当我尝试应用上述方法时,我得到了一个错误

    System.InvalidOperationException:子级/依赖方无法 确定为一对一的关系

    0 回复  |  直到 5 年前
        1
  •  0
  •   Serge    5 年前

    如果会话可以包含多条消息,则称为一对多关系。 你必须修理桌子:

    
    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
  •  0
  •   user3163495    5 年前

    在尝试了各种各样的数据注释和fluentapi之后,我能想到的最干净的解决方案是非常简单的,这两种方法都不需要。它只需要将“private”构造函数添加到将“DbContext”对象注入的Conversation类(如果使用延迟加载,则添加“protected”构造函数)。只需将“Conversation”和“Message”类设置为一个普通的一对多关系,并且现在可以从“Conversation”实体中获得数据库上下文,就可以使用Find()方法使“LastMessage”简单地从数据库返回查询。Find()方法还使用缓存,因此如果多次调用getter,它只会访问数据库一次。

    以下是有关此功能的文档: 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; }
    }