【问题标题】:Entity Framework Core - one-to-many but parent also has navigation property to a single child?实体框架核心 - 一对多但父级也具有单个子级的导航属性?
【发布时间】:2021-02-17 01:29:17
【问题描述】:

我目前在实体“对话”和“消息”之间建立了有效的一对多关系,其中一个对话可以包含多条消息。

这很好用:

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”的“对话”类中,它将跟踪创建的最后一条消息记录:

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

当我尝试应用上述内容时,我得到了错误

System.InvalidOperationException:子/依赖方不能 确定之间的一对一关系 “Conversation.LastMessage”和“Message.Conversation”。

如何保持“对话”和“消息”之间的一对多关系,但还要在“对话”类中添加导航属性以导航到单个“消息”记录?

【问题讨论】:

    标签: asp.net-core entity-framework-core foreign-keys


    【解决方案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】:

      在尝试了各种数据注释和 Fluent API 废话之后,我能想出的最干净的解决方案被证明是非常简单的,这两者都不需要。它只需要向注入“DbContext”对象的会话类(或“受保护”类,如果您使用延迟加载)添加一个“私有”构造函数。只需将“对话”和“消息”类设置为正常的一对多关系,并且现在可以从“对话”实体中获得数据库上下文,您可以使“LastMessage”简单地从数据库返回查询使用 Find() 方法。 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; }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-24
        • 1970-01-01
        • 2016-06-05
        • 2017-07-26
        • 1970-01-01
        相关资源
        最近更新 更多