【问题标题】:Fluent NHibernate: Issue with many-to-many relationship with custom intermediary tableFluent NHibernate:与自定义中间表的多对多关系问题
【发布时间】:2013-04-07 17:31:57
【问题描述】:

(更新见底部)

所以,我有两个对象(我们称它们为 ClassA 和 ClassB),它们表现出多对多的关系(ClassA 可以有多个 ClassB 对象,反之亦然)。

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

因此,我没有将其定义为 ClassA 和 ClassB 之间的多对多,而是定义了两个一对多关系(一个在 ClassA 上,一个在 ClassB 上)指向第三个中间类(我们'我称之为“关系”)。在结构上,我希望这些表格看起来像这样:

     ClassA                      Relationship      
----------------              ----------------
      Id (PK)                       Id (PK)
     Name                          Name
  Description                    StartDate
                                 ClassA_Id (FK)
    ClassB                       ClassB_Id (FK)
---------------- 
      Id (PK)    
     Name        
  Description

为了实现这一点,我设置了这样的映射(经过 ton 的反复试验):

public ClassAMap()
{
    Id(x => x.Id);
    Map(x => x.Name);
    HasMany(x => x.Relationship)    
        .KeyColumn("ClassA_Id")
        .ForeignKeyConstraintName("FK_Relationship_ClassA")
        .LazyLoad()
        .Cascade.All()
        .Inverse();
}

public ClassBMap()
{
    Id(x => x.Id);
    Map(x => x.Name);
    HasMany(x => x.Relationship)    
        .KeyColumn("ClassB_Id")        
        .ForeignKeyConstraintName("FK_Relationship_ClassB")
        .LazyLoad()
        .Cascade.All()
        .Inverse();
}

public RelationshipMap()
{
    Id(x => x.Id);
    Map(x => x.CreatedDate);
    References(x => x.ClassAObject)
        .Column("ClassA_Id")
        .Cascade.SaveUpdate();
    References(x => x.ClassBObject)
        .Column("ClassB_Id")
        .Cascade.SaveUpdate();
}

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

但是,如果我只是尝试从其中一个父对象(例如 classA.Relationships.Remove(relationshipObject); classARepository.Update(classA);)中删除关系,则会收到类似于以下内容的错误:

NHibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [ClassB]

NHibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations) [Relationship#9]

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

如何更改映射以实现我正在寻找的行为?

作为参考,以下是必须通过的场景的大纲:

  1. 添加具有关系的新 ClassA
    • 添加或更新了 A/B 类/关系的记录
  2. 删除现有的 A 类
    • A 类和关系的记录已删除
    • B 类记录保留在表中
  3. 从 ClassA.Relationships 列表中删除关系;更新 A 类
    • 从关系表中删除关系
    • ClassA 和 ClassB 记录保留

更新:根据@JamieIde 的回答,我提出了以下调整:

  • 取消关系上对 ClassA 和 ClassB 的引用:
// NOTE, this is an example snippet to show the steps that need to be taken

using(var session = SomeSessionFactory().OpenSession())
{
    using (var transaction = session.BeginTransaction())
    {
        // preferably place these four lines in a public method on one of the objects
        classAObject.Relationships.Remove(relationship);
        classBObject.Relationships.Remove(relationship);
        relationship.ClassA = null;
        relationship.ClassB = null;

        transaction.Commit();
    }
}   
  • 将 ClassA/ClassB 上的 .Cascade.All() 更改为 Cascade.AllDeleteOrphan()。这可以防止 NHibernate 在清空关系对象上的引用后将空白记录插入到 ClassA 和 ClassB 表中
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();
}

【问题讨论】:

    标签: c# nhibernate fluent-nhibernate nhibernate-mapping fluent-nhibernate-mapping


    【解决方案1】:

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

    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 就会获取对象更改(我对 NHibertate 很陌生)。我将使用工作代码更新我的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    相关资源
    最近更新 更多