代码之家  ›  专栏  ›  技术社区  ›  Natrium

如何在代码中建模多对多关系?

  •  23
  • Natrium  · 技术社区  · 16 年前

    假设我在一个数据库中有两个表。 例如:狗和老板 这是一种多对多的关系,因为一个老板可以有一只以上的狗,一只狗可以有一个以上的主人。我是鲍比的主人,但我妻子也是。

    但是很多对很多是不允许的,所以有一个可以帮助的:狗老板

    如何在代码中建模?

    班长可以养狗。 班狗可以有一群老板。 --&至少,我是这么想的。也许有更好的解决方案?

    helper表中的额外数据如何? 那应该是在德博斯班还是在狗班? 昵称 (我叫狗“好孩子”,我妻子叫它“狗”)。

    我希望我的问题有点清楚? 在实现这一目标的最佳方法方面,是否有任何最佳实践? 你能给我一些推荐信吗?

    ORM(如NHibernate)不是一个选项。

    12 回复  |  直到 11 年前
        1
  •  21
  •   John Saunders    11 年前

    你为什么说桌子?您是在创建对象模型还是数据库模型?

    对于对象模型,狗没有理由不能 List<Owner> 一个拥有者有一个 List<Dog> . 只有在关系上有属性时,才需要一个中间类(UML称之为关联类)。当你有一个带有额外属性的DogOwnership类时,每个所有者都会有一个 List<DogOwnership> 每只狗也一样。狗主人会有一只狗,一个主人,以及额外的财产。

        2
  •  13
  •   James    11 年前
    public class Boss
    {
       private string name;
       private List<Hashtable> dogs;
       private int limit;
    
       public Boss(string name, int dogLimit)
       {
          this.name = name;
          this.dogs = new List<Hashtable>();
          this.limit = dogLimit; 
       }
    
       public string Name { get { return this.name; } }
    
       public void AddDog(string nickname, Dog dog)
       {
          if (!this.dogs.Contains(nickname) && !this.dogs.Count == limit)
          {
             this.dogs.Add(nickname, dog);
             dog.AddBoss(this);
          } 
       }
    
       public void RemoveDog(string nickname)
       {
           this.dogs.Remove(nickname);
           dog.RemoveBoss(this);
       }
    
       public void Hashtable Dogs { get { return this.dogs; } }
    }
    
    public class Dog
    {
       private string name;
       private List<Boss> bosses;
    
       public Dog(string name)
       {
          this.name = name;
          this.bosses = new List<Boss>();
       }
    
       public string Name { get { return this.name; } }
    
       public void AddBoss(Boss boss)
       {
          if (!this.bosses.Contains(boss))
          {
              this.bosses.Add(boss);
          }
       }
    
       public void RemoveBoss(Boss boss)
       {
          this.bosses.Remove(boss);
       }  
    
       public ReadOnlyCollection<Boss> Bosses { get { return new ReadOnlyCollection<Boss>(this.bosses); } }
    }
    

    上述观点认为,老板之间的关系可以有多只狗(有一个限制)和多只狗有多个老板。这也意味着,当老板添加狗时,他们可以为狗指定一个昵称,该昵称仅对老板是唯一的。这意味着其他老板可以添加相同的狗,但有不同的昵称。

    至于限制,我可能会将其作为app.config值,在实例化boss对象之前,您只需读取该值。所以一个小例子是:

    var james = new Boss("James", ConfigurationManager.AppSettings["DogsPerBoss"]);
    var joe = new Boss("Joe", ConfigurationManager.AppSettings["DogsPerBoss"]);
    
    var benji = new Dog("Benji");
    var pooch = new Dog("Pooch");
    
    james.AddDog("Good boy", benji);
    joe.AddDog("Doggy", benji);
    
    james.AddDog("Rover", pooch);
    joe.AddDog("Buddy", pooch);  // won't add as the preset limit has been reached.
    

    很明显,你可以根据自己的情况来调整这个,但是,我认为你所要寻找的东西的基本原理是存在的。

    • 老板可以养多只狗
    • 狗可以有多个老板
    • 老板对同一条狗可以有不同的绰号。
        3
  •  5
  •   Frederik Gheysels    16 年前

    像这样的东西; 不过,它仍然需要一些微调(例如,将集合设为私有,并为它添加一个只读公共访问器,该访问器返回一个readOnlyCollection,但您将捕获这种偏差。

    public class Dog
    {
        public List<Boss> Bosses;
    
        public void AddBoss( Boss b )  
        {
            if( b != null && Bosses.Contains (b) == false )
            {
                Bosses.Add (b);
                b.AddDog (this);
            }
        }
    
        public void RemoveBoss( Boss b )
        {
             if( b !=null && Bosses.Contains (b) )
             {
                 Bosses.Remove (b);
                 b.RemoveDog (this);
             }
        }
    }
    
    public class Boss
    {
        public List<Dog> Dogs;
    
        public void AddDog( Dog d )
        {
             if( d != null && Dogs.Contains (d) == false )
             {
                  Dogs.Add(d);
                  d.AddBoss(this);
             }
        }
    
        public void RemoveDog( Dog d )
        {
            if( d != null && Dogs.Contains(d) )
            {
                Dogs.Remove (d);
                d.RemoveBoss(this);
            }
        }
    }
    

    通过这种方式,你可以在你的代码中建立一个多对多的模型,每个狗都认识他的老板,每个老板都认识他的狗。 当您需要helper表中的额外数据时,您还需要创建另一个类。

        4
  •  1
  •   MrTelly    16 年前

    这是数据库之间的一个经典问题,其中多对多不起作用,因此您的助手表,以及对象世界,其中多对多起作用。一旦关系具有属性,那么您应该创建一个新类来保存该信息。但是,如果您查看对象关系映射(ORM),您将节省大量的工作时间,整个字段都是为了解决DB和对象之间的这个(以及许多其他)问题而长大的。

        5
  •  1
  •   Jamie Ide    16 年前

    如果您有一个简单的多对多链接表,其中包含关系中每个表的外键,那么您可以按照您的建议对其进行建模:boss有一组狗,dog有一组boss。

    如果您与额外数据(如昵称)有多对多关系,那么您可以将其建模为两个一对多关系。创建一个实体,例如dog boss,以便boss拥有dog boss集合,dog拥有dog boss集合。

        6
  •  1
  •   John Nicholas    16 年前

    传统的多对多关系在匹配表上没有额外的字段。

    因为你的领域有独特的信息,我倾向于停止思考这些关系一样多对多。

    一旦您将信息添加到匹配表中,我认为您已经使这个表本身成为一个实体,因此需要它自己的对象来表示它。

    此时,您可以开始有一个dogsname类来连接一个人和一只狗——这两个类都将包含对该对象的引用,作为集合的一部分。

    然而,无论你给狗起一个名字,让它自己或叫自己的狗都是独立的。

    除了根据不同的人来模拟狗的名字之间的关系,你还需要模拟狗的主人关系。在内存中,这意味着两个对象都包含其他对象的列表。

        7
  •  1
  •   Graham    16 年前

    如果你不需要记录下这个绰号,那么狗应该有老板的名单,老板应该有狗的名单。

    如果dog和boss之间的关系具有属性(在本例中是昵称),那么应该创建一个类来表示该关系,并让dog和boss都持有该类型的列表。

    我一直在使用 NHibernate 暂时来看,它对缓解这种状况非常有用。 object relational impedance mismatch .

        8
  •  0
  •   Greg Dean    16 年前

    我想是少了点什么。为什么不允许多对多?

    public class Boss
    {
        Dog[] dogs;
    }
    
    public class Dog
    {
        Boss[] bosses;
    }
    
        9
  •  0
  •   CraigTP    16 年前

    在关系模型中,建立多对多关系模型的最佳方法(使用狗/老板的例子)是有三个单独的表。

    一张桌子放狗,一张桌子放老板(每张桌子都有一把钥匙),第三张桌子通常是 junction table “。

    此表通常至少有两个字段,一个字段用于狗的外键,另一个字段用于老板的外键。 这样一来,每只狗可以有许多个老板,每个老板可以有许多狗。

    现在,当以一种更面向对象的方式在代码中建模时,这通常是通过拥有一个dog类和一个boss类来实现的。 除了具有这些对象的通常原子属性外,每个对象都将 还公开属于另一个的集合的属性。

    因此,例如,一个狗对象会有一个称为“老板”的财产。此属性将公开分配给特定Dog对象(在联接表中定义)的boss对象集合,而在另一方面,每个boss对象将公开一个名为Dogs的属性,该属性将是分配给特定Boss对象(由联接表定义)的Dog对象集合。

    请注意,这些对象中可能存在一些“重叠”(即一个“dog”对象可能具有另一个“dog”对象所具有的“boss”对象),但是,这是将三表多对多关系模型转换为面向对象模型的传统机制。

        10
  •  0
  •   Mike Burton    16 年前

    我是否遗漏了一些内容,或者您需要的唯一代码如下:

    List<Bosses> BossList;
    
    class Dog {}
    class Boss { Dog[] Dogs; }
    

    您不需要显式地为双向关系建模。它隐含在代码结构中。这样做可能还有其他原因,但一般来说,拥有单向引用和遍历引用对象集的方法就足够了。

        11
  •  0
  •   Murat YILMAZ    15 年前

    每次我们需要思考现实生活和我们的需要。在这种情况下,关键是哪一个应该有另一个。

    在现实生活中,一条狗和一个老板可能没有彼此。但是你的软件需求会影响这种关系。

    • 例如,如果您正在开发兽医病患管理软件,对于治疗流浪狗的兽医,那么病患(狗)-监护人(老板)关系应如下所示: 老板必须至少有一个狗和狗可能没有任何老板(那么老板ID是这段关系的外门钥匙),这意味着在你的设计狗类必须拥有一个老板集合。为什么,因为没有任何狗就不能创建任何boss实例。我们也可以用数据逻辑来做出这个决定。当您试图在数据库中保存您的狗和老板类时,让我们考虑一下。如果关系条件如上所述,在保存凸台时,应将连接记录插入到连接表中。

    • 如果你正在开发一个不治疗流浪狗的兽医软件,那么父母与病人的关系应该是这样的: 一只狗必须至少有一个老板和老板必须至少有一只狗,我们需要考虑这个特殊的关系案例。这意味着任何这些类实例都不能没有彼此创建。所以我们需要在OO设计中定义这个特性。这意味着我们需要一个表示这种依赖性的类。当然,这种依赖关系将存储在连接表中。

    -如果您的软件是为一个兽医开发的,他可以治疗流浪狗和这些被老板采纳的狗,那么您的设计应该是这样的: 任何狗在领养前都可能没有老板,任何老板也可能没有狗。在这种情况下,我们的OO设计需要关注这个特殊情况。这个案子和第一个有点相似。所以我们可以将任何类的集合添加到另一个类中。但是,像这样的任何软件需求都会影响到其他需求。比如报告。如果兽医担心被老板收养的狗,他迟早会要求报告谁收养了哪只狗。就像在句子中,(狗被老板收养)如果狗类包含老板类的集合就更好了。

    我希望我能对你的问题给出正确的答案。

        12
  •  -1
  •   waqasahmed    16 年前

    不知道你要什么。但这是您想要的表结构:

    狗桌

    DoGuiID int pk 狗名varchar(50)

    狗精老板

    身份识别 密码输入 博斯米德国际贸易组织 狗昵称varchar(15)

    老板

    BassIdID int pk 老板姓名varchar(50)