流畅的NHibernate:与自定义中介表的多对多关系问题

时间:2013-04-15 14:36:44

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

(请参阅底部的更新)

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

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

因此,我没有将它定义为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,将它们通过Relationship链接然后保存ClassA,则所有相关记录都会添加到这三个表中。类似地,如果我删除相同的ClassA,则删除ClassA记录和Relationship记录,但仍保留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
    • 已添加或更新的ClassA / B /关系记录
  2. 删除现有的ClassA
    • 已删除ClassA和关系的记录
    • ClassB记录保留在表
  3. 从ClassA.Relationships列表中删除关系;更新ClassA
    • 从关系表中删除关系
    • ClassA和ClassB记录仍为
  4. 更新:根据@ 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();
    }
    

1 个答案:

答案 0 :(得分:1)

您不需要更改映射,您需要使对Relationship对象中的一侧的引用为空。

classA.Relationships.Remove(relationshipObject);
relationshipObject.ClassAObject = null;
session.Flush();

基本上你需要小心维护关系的两面,以便内存中的对象图是正确的,并且可以由NHibernate持久化。通常的做法是封装这个:

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

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