C# Fluent NHibernate:与自定义中间表的多对多关系问题

C# Fluent NHibernate:与自定义中间表的多对多关系问题,c#,nhibernate,fluent-nhibernate,nhibernate-mapping,fluent-nhibernate-mapping,C#,Nhibernate,Fluent Nhibernate,Nhibernate Mapping,Fluent Nhibernate Mapping,(更新见底部) 所以,我有两个对象(我们称它们为ClassA和ClassB),它们展示了多对多关系(ClassA可以有多个ClassB对象,反之亦然) 但是,与传统的多对多不同,实际的“关系”本身是一个对象,具有关于其他两个类之间链接的不同信息(关系开始日期、关系名称等) 因此,我没有将其定义为类a和类B之间的多对多关系,而是定义了两个一对多关系(一个在类a和类B上),指向第三个中间类(我们称之为“关系”)。从结构上看,我希望这些表如下所示: ClassA

(更新见底部)

所以,我有两个对象(我们称它们为ClassA和ClassB),它们展示了多对多关系(ClassA可以有多个ClassB对象,反之亦然)

但是,与传统的多对多不同,实际的“关系”本身是一个对象,具有关于其他两个类之间链接的不同信息(关系开始日期、关系名称等)

因此,我没有将其定义为类a和类B之间的多对多关系,而是定义了两个一对多关系(一个在类a和类B上),指向第三个中间类(我们称之为“关系”)。从结构上看,我希望这些表如下所示:

     ClassA                      Relationship      
----------------              ----------------
      Id (PK)                       Id (PK)
     Name                          Name
  Description                    StartDate
                                 ClassA_Id (FK)
    ClassB                       ClassB_Id (FK)
---------------- 
      Id (PK)    
     Name        
  Description
为了实现这一点,我已经这样设置了映射(经过大量的尝试和错误):

现在,对于添加/删除父对象,his完全按照预期工作。例如,如果我创建了一个新的ClassA和ClassB,通过关系将它们链接起来,然后保存ClassA,那么所有关联的记录都会添加到这三个表中。类似地,如果我删除了相同的ClassA,那么ClassA记录和关系记录将被删除,但ClassB记录将保留(预期行为)

但是,如果我只是尝试从父对象之一(例如
classA.Relationships.remove(relationshipObject);classARepository.Update(classA);
)中删除关系,则会出现类似以下错误:

NHibernate.StaleObjectStateException:行被另一个事务更新或删除(或未保存的值映射不正确):[ClassB]

NHibernate.ObjectDeletedException:删除的对象将通过级联重新保存(从关联中删除删除的对象)[Relationship#9]

此外,如果我完全从数据库中重新加载ClassA对象,并如上所述删除成员资格,那么它将无误地执行,但我的关系不会从数据库中删除

我如何改变映射以实现我想要的行为

以下概述了必须通过的场景,以供参考:

  • 添加具有关系的新ClassA
    • 已添加或更新类别A/B/关系的记录
  • 删除现有的ClassA
    • 已删除ClassA和关系的记录
    • ClassB记录保留在表中
  • 从ClassA.Relationships列表中删除关系;更新ClassA
    • 从关系表中删除关系
    • A类和B类记录保留
  • 更新:根据@JamieIde的回答,我做出了以下调整:

    • 在关系上取消对ClassA和ClassB的引用:
    • 将ClassA/ClassB上的
      .Cascade.All()
      更改为
      Cascade.AllDeleteOrphan()
      。这可以防止NHibernate在清空关系对象上的引用后将空白记录插入ClassA和ClassB表中

    您不需要更改映射,您需要将对关系对象中一侧的引用设为null

    classA.Relationships.Remove(relationshipObject);
    relationshipObject.ClassAObject = null;
    session.Flush();
    
    基本上,您需要小心维护关系的两侧,以便内存中的对象图是正确的,并且可以由NHibernate持久化。通常的做法是封装以下内容:

    void RemoveRelationship(RelationshipObject relationshipObject)
    {
        Relationships.Remove(relationshipObject);
        relationshipObject.ClassAObject = null;
    }
    

    此外,您不太可能需要调用
    Update
    ,只刷新会话或(更好的是)提交事务。

    +1,这无疑让我走上了正确的轨道(我必须将父对象上的
    Cascade.All()
    更改为
    Cascade.AllDeleteOrphan()
    ,因为仅将父对象置零就导致NHibernate在ClassA/ClassB表中插入了空记录。此外,我没有意识到在不显式调用update的情况下会获取对象更改(我对NHibernate非常陌生)。我将用工作代码更新我的问题。
    public ClassAMap()
    {
        Id(x => x.Id);
        Map(x => x.Name);
        HasMany(x => x.Relationship)    
            .KeyColumn("ClassA_Id")
            .ForeignKeyConstraintName("FK_Relationship_ClassA")
            .LazyLoad()
            .Cascade.AllDeleteOrphan() // here's the key line
            .Inverse();
    }
    
    classA.Relationships.Remove(relationshipObject);
    relationshipObject.ClassAObject = null;
    session.Flush();
    
    void RemoveRelationship(RelationshipObject relationshipObject)
    {
        Relationships.Remove(relationshipObject);
        relationshipObject.ClassAObject = null;
    }