关于多对多关系的OO问题(规划NHibernate)

时间:2009-08-13 17:31:19

标签: c# nhibernate oop

我正在设计一个OO对象模型,计划使用NHibernate作为数据访问层。

在处理两个具有多对多关系的实体时,我想知道最好的OO设计(特别是为了与NHibernate轻松集成)。

对象: 用户 - 单个用户可以与多个主题相关 主题 - 单个主题可以与多个用户相关

在SQL中,使用多对多表格可以直接实现这种关系;

tblUser
  userID

tblSubject
  subjectID

tblUserSubject
  userSubjectID
  userID
  subjectID

那么,应该如何创建对象?每个对象是否应包含另一个对象的集合?例如:

class User 
{
   public int userID {get; set;}
   public List<Subject> subjects {get; set;}
}

class Subject
{
   public int subjectID {get; set;}
   public List<User> users {get; set;}
}

有没有更好的方法对此进行建模,以便NHibernate可以轻松地保持关系?

7 个答案:

答案 0 :(得分:3)

对于NHibernate最好的设计,我没有答案,但我想发表评论,因为这让我想起了很多Rob Conery关于域驱动设计的Hanselminutes的讨论。

我的内心反应是,如果您的用户在您的主题包含用户的同时包含主题,则某些内容不正确。我想把它缩小到一个或另一个。有趣的是,Conery正在讨论他的店面应用以及关于产品是否有类别或类别是否有产品的争论。事实证明既不是就是这种情况 - 它实际上是一个切线关系,最好由应用程序中的服务处理。 (至少,这是实施它的最佳DDD方式,因为它是客户与实体相关的方式。)

所以,除了DDD,我想知道它是否会帮助你仔细研究用户和主题之间真正的关系,以及是否甚至包含其他关系。由于我有点不喜欢每个包含另一个的想法,我可能会考虑更频繁地使用哪个子集合。检索用户时,您是否经常使用其主题?相反,虽然主题可能有与之相关的用户,但是在对主题对象进行操作时是否经常使用此集合?如果没有,也许该集合不直接属于它。

同样,我无法证明哪种设计最适合NHibernate,但我认为这是值得考虑的事情。

答案 1 :(得分:1)

找到一篇你会发现有帮助的文章。 http://codebetter.com/blogs/peter.van.ooijen/archive/2008/05/29/nhibernate-many-to-many-collections-or-mapping-is-not-one-table-one-class.aspx

总结一下您的解决方案看起来非常相似。为了完整性,这里的nHibernate映射文件可能是您的User对象的样子。将nHibernate映射集合用于用户和主题列表。

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="YourAssemblyName" namespace="YourNamespace">
  <class name ="User" table="Users" proxy="User">
    <id name="Id" type="Int32" column="idUser">
      <generator class="identity"></generator>
    </id>                
    <many-to-one name="CreatedBy" class="User" column="idUser"></many-to-one>
    <bag name="subjects" table="tblUserSubject" lazy="false" >
      <key column="idUser"></key>
      <many-to-many class="Subject" column="idSubject"></many-to-many>
    </bag>
  </class>
</hibernate-mapping> 

科目是一个使用表tblUserSubject的包。此链接表的关键是idUser(假设这是您的标识列名称)。多对多的类是我们刚刚映射的Subject类。

答案 2 :(得分:1)

首先避免多对多。为什么以及如何看看http://www.udidahan.com/2009/01/24/ddd-many-to-many-object-relational-mapping/

答案 3 :(得分:1)

如果User引用了所有Subject个,Subject引用了所有User,那么当您阅读单个用户时:

  • 阅读该用户的详细信息;
  • 读取该用户的主题;
  • 阅读每个主题的用户;
  • 阅读每个主题的用户主题;
  • 阅读每个主题的用户主题用户;
  • [...]

除非你对延迟加载非常小心,否则你最终只能加载一大块两个表来查看单个用户记录。

我之前没有使用过NHibernate,但是在Java Hibernate中你可以通过以下方式解决这个问题:

  • 使用延迟加载或
  • 用以下内容替换多对多:
    • 两个新类UserSummarySubjectSummary绑定到相同的表,但没有多对一关系,
    • 更改User以引用一堆SubjectSummarySubject来引用一堆UserSummary s。

答案 4 :(得分:0)

这应该可以解决问题,但是根据您是否确实需要知道主题上的哪些用户可用,或者对于用户而言,可能会遗漏一个主题。

答案 5 :(得分:0)

你已经有了支持多对多的基本结构。不要忘记添加正确连接关联的方法:

class User 
{
   public int userID {get; set;}
   public List<Subject> subjects {get; set;}

   public void AddSubject(Subject subject)
   {
       subject.Users.Add(this);
       this.Subjects.Add(subject);
   }
}

class Subject
{
   public int subjectID {get; set;}
   public List<User> users {get; set;}

   public void AddUser(User user)
   {
       user.Subjects.Add(this);
       this.Users.Add(user);
   }
}

答案 6 :(得分:0)

我今天在Kuate等人的 NHibernate in Action 中找到了以下内容。 (第60页和第61页;我更改了书中的对象名称,以保持原始示例的完整性。)

这与Vijay的答案类似,但看起来NHiberate喜欢看到一个接口而不是集合的列表类型。

class User 
{
   public int userID {get; set;}
   private ISet subjects = new HashedSet();

   public ISet subjects {
      get { return subjects; }
      set { subjects = value; }
   }

   public void AddSubject(Subject subject)
   {
       subject.Users.Add(this);
       this.Subjects.Add(subject);
   }
}

class Subject
{
   public int subjectID {get; set;}
   private ISet users = new HashedSet();

   public ISet users {
      get { return users; }
      set { users = value; }
   }

   public void AddUser(User user)
   {
       user.Subjects.Add(this);
       this.Users.Add(user);
   }
}