代码之家  ›  专栏  ›  技术社区  ›  Iain Holder

自联接关系具有附加属性时的NHibernate映射

  •  5
  • Iain Holder  · 技术社区  · 17 年前

    如何将一个类映射到同一类的其他实例 当这种关系本身具有属性时 ?

    我有一个名为Person的类,它映射到一个表Person

    PersonID   PersonName    PersonAge 
    ----------------------------------
           1   Dave Dee             55
           2   Dozy                 52
           3   Beaky                45
           4   Mick                 55
           5   Tich                 58
    

     PersonPersonID  PersonID  RelatedPersonID RelationshipID 
     --------------------------------------------------------
                  1         1                5              1
                  2         3                4              2
                  3         2                1              3
    

    我希望PersonPerson表中包含以下属性:

    RelationshipID  RelationshipName
    --------------------------------
                 1  Colleague
                 2  Manager
                 3  Tutor
    

    This question 以及与 post by Billy McCafferty 说明由于PersonPerson表中的附加列,PersonPerson关系必须以其自身的权限从普通联接提升到实体。然而,它并没有解释当它是自联接时应该做什么。区别在于如果我要求所有相关人员 戴夫·迪 提奇 (ID=5),但我也应该得到 昏昏欲睡 (ID=2)因为Dave Dee也在RelatedPersonID列中。

    public virtual IList<PersonPerson> PersonPersonForward {get;set;}
    public virtual IList<PersonPerson> PersonPersonBack {get;set;}
    
    private List<PersonPerson> personPersonAll;
    public virtual List<PersonPerson> PersonPersonAll 
    {
       get
       {
           personPersonAll = new List<PersonPerson>(PersonPersonForward);
           personPersonAll.AddRange(PersonPersonBack);
           return personPersonAll;
       }
    }
    

    并在hbm中具有以下内容:

     <bag name="PersonPersonForward" table="PersonPerson" cascade="all">
          <key column="PersonID"/>
          <one-to-many class="PersonPerson" />
     </bag>
    
     <bag name="PersonPersonBack" table="PersonPerson" cascade="all">
          <key column="RelatedPersonID"/>
          <one-to-many class="PersonPerson" />
     </bag>
    

    这似乎有点笨重和不雅。NHibernate通常为大多数日常问题提供优雅的解决方案。上述方法是明智的还是有更好的方法?

    2 回复  |  直到 9 年前
        1
  •  2
  •   Jeffrey Hantin    17 年前

    在我看来,你基本上建立了一个 directed graph ,以及这两个映射 PersonPersonForward PersonPersonBack 分别表示传出边和传入边。

    这种定向性通过关系类型的语义得到加强:while 是同事吗 symmetric relation 是一个经理吗 是导师吗 它们几乎肯定是不对称的。

        2
  •  2
  •   Frederik Gheysels    17 年前

    我想我也会这样做,但是,我认为这样建模有点“笨拙”。 我的意思是:你有一群与某个人有关系的人,但你也有一个“后台关系”。
    这真的有必要吗?是否可以选择删除此后台集合,并在PersonRepository上指定一个方法,该方法可以将与给定人员有某种关系的所有人员返回?

    嗯,这听起来可能有点模糊,所以这里有一些代码(注意,为了简洁起见,我省略了“虚拟”修饰符等…(我也不喜欢使用这些修饰符,所以在99%的时间里,我在类映射中指定“lazy=false”)。

    public class Person
    {
        public int Id {get; set;}
        public string Name {get; set;}
    
        public IList<PersonPerson> _relatedPersons;
    
        public ReadOnlyCollection<PersonPerson> RelatedPersons
        {
            get
            {
               // The RelatedPersons property is mapped with NHibernate, but
               // using its backed field _relatedPersons (can be done using the 
               // access attrib in the HBM.
               // I prefer to expose the collection itself as a readonlycollection
               // to the client, so that RelatedPersons have to be added through
               // the AddRelatedPerson method (and removed via a RemoveRelatedPerson method).
    
               return new List<PersonPerson) (_relatedPersons).AsReadOnly();
            }
        }
    
        public void AddRelatedPerson( Person p, RelationType relatesAs )
        {
           ...
        }
    
    }
    

    如您所见,Person类只剩下一个集合,即PersonPerson对象的集合,它表示此人拥有的关系。 为了获取与给定人员有关系的人员,您可以在PersonRepository上创建一个返回这些人员的特定方法,而不是将他们放在Person类的集合中。我认为这也会提高性能。

    public class NHPersonRepository : IPersonRepository
    {
        ...
    
        public IList<Person> FindPersonsThatHaveARelationShipWithPerson( Person p )
        {
            ICriteria crit = _session.CreateCriteria <Person>();
    
            crit.AddAlias ("RelatedPersons", "r");
    
            crit.Add (Expression.Eq ("r.RelatedWithPerson", p));
    
            return crit.List();
    
        }
    }
    

    “反向引用”不是Person类的成员;它必须通过存储库进行访问。 这也是Eric Evans在他的DDD-book中所说的:在某些情况下,最好在存储库上有一个专门的方法,可以让您访问相关对象,而不是让它们(=相关对象)随身携带对象本身。