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

时间:2009-07-09 12:55:33

标签: c# many-to-many modeling

假设我在数据库中有2个表。 例如:Dog&老板 这是一个多对多的关系,导致一个老板可以拥有超过1只狗,而一只狗可以拥有超过1个主人。我是鲍比的老板,但我的妻子也是。

但是不允许多对多,所以有一个很好的帮助:DogsPerBoss

如何在代码中对此进行建模?

Class Boss可以拥有一系列Dogs。 Class Dog可以拥有一系列老板。 - >至少,这就是我的想法。也许有更好的解决方案?

辅助表中的额外数据怎么样? 应该是de Boss级还是Dog级? 例如:昵称 (我把狗称为“好男孩”,我的妻子称他为“小狗”)

我希望我的问题有点清楚吗? 有什么最好的方法来实现这个目标是什么? 你能给我一些参考资料吗?

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

12 个答案:

答案 0 :(得分:22)

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

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

答案 1 :(得分:13)

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.

你可以在你认为合适的情况下调整它,但是,我认为你所寻找的基本原理是存在的。

  • 老板可以有多只限量狗
  • 狗可以有多个老板
  • 老板可以为同一只狗设置不同的昵称。

答案 2 :(得分:5)

像这样的东西; 它仍然需要一些微调(将集合设为私有并为其添加一个只读的公共访问器,例如返回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);
        }
    }
}

通过这种方式,您可以在代码中对多对多进行建模,其中每只狗都知道他的老板,并且每个Boss都知道他的狗。 当您需要辅助表中的额外数据时,您还需要创建另一个类。

答案 3 :(得分:1)

如果您不需要记录昵称,那么Dog应该有一份老板名单,Boss应该有一份狗名单。

如果Dog和Boss之间的关系具有属性,在这种情况下是昵称,那么你应该创建一个表示该关系的类,并让Dog和Boss都保存该类型的列表。

我一直在使用NHibernate一段时间,发现它对于缓解此类object relational impedance mismatch非常有用。

答案 4 :(得分:1)

这是数据库之间的经典问题,其中多对多不起作用,因此您的帮助程序表,以及多对多工作正常的对象世界。一旦关系具有属性,那么您应该创建一个新类来保存该信息。但是,如果你看一下对象关系映射 - ORM,那么你将节省很多时间,整个领域都是为了解决DB和Object之间的这个(以及许多其他)问题。

答案 5 :(得分:1)

如果你有一个简单的多对多链接表,其中包含来自关系中每个表的外键,那么你可以按照你的建议对其进行建模:Boss有一系列Dogs and Dog有一个老板的集合。

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

答案 6 :(得分:1)

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

因为你确实拥有具有独特信息的字段,所以我倾向于不再考虑这些关系。

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

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

然而,你是否给狗一个名字叫狗或拥有狗是独立的。

除了根据不同的人建模狗名称的关系之外,您还需要对所有权关系进行建模。在内存中,这意味着两个对象都包含其他对象的列表。

答案 7 :(得分:0)

我想我错过了什么。为什么许多人不允许?

public class Boss
{
    Dog[] dogs;
}

public class Dog
{
    Boss[] bosses;
}

答案 8 :(得分:0)

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

一个DOGS表,一个BOSSES表(每个表都有一个唯一键),第三个表通常是“junction table”。

这个表通常至少有两个字段,一个字段用于Dog的外键,另一个字段用于Boss的外键。 这样每只狗都可以拥有很多老板,每个老板都可以拥有很多狗。

现在,当它以更面向对象的方式在代码中对其进行建模时,通常通过使用Dog类和Boss类来实现。 除了为每个这些对象提供通常的原子属性外,每个对象都有 还暴露了另一个属性的属性。

因此,例如,Dog对象将具有名为“Bosses”的属性。此属性将公开分配给特定Dog对象的Boss对象集合(如联结表中所定义),另一方面,每个Boss对象将公开名为Dogs的属性,该属性将是已分配的Dog对象的集合到特定的Boss对象(由联结表定义)。

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

答案 9 :(得分:0)

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

List<Bosses> BossList;

class Dog {}
class Boss { Dog[] Dogs; }

您无需明确建模双向关系。它隐含在代码结构中。可能还有其他原因,但一般情况下,单向引用和遍历引用对象集的方法就足够了。

答案 10 :(得分:0)

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

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

  • 例如,如果您正在开发一个兽医患者管理软件,对于治疗流浪狗的兽医来说,患者(狗) - 监护人(老板)的关系应该是这样的: 老板必须至少有一只狗和狗可能没有任何老板(然后boss id是这种关系的外键),这意味着在你的设计中,狗类必须拥有一组老板。为什么因为没有任何狗都无法创建任何boss实例。我们也可以使用数据逻辑来做出这个决定。让我们考虑一下您何时尝试将您的狗和老板课程保存在数据库中。如果上述关系条件,在保存boss时应将联结记录插入联结表中。

  • 如果你正在开发一个不治疗流浪狗的兽医那个软件,那么患者 - 父母的关系必须是这样的: 一只狗必须至少有一个老板,老板必须至少有一只狗,我们需要考虑这种特殊情况。这意味着如果没有彼此,就无法创建任何这些类实例。所以我们需要在OO设计中定义这个特性。这意味着我们需要一个代表这种依赖关系的类。当然,这种依赖关系将存储在联结表中。

- 如果您的软件是为治疗流浪狗的兽医开发的,而这些狗是由老板采用的,那么您的设计应该是这样的:  任何狗可能没有老板,任何老板可能没有任何狗,直到收养。在这种情况下,我们的OO设计需要关注这种特殊情况。这种情况与第一种情况略有不同。所以我们可以将任何类的集合添加到其他类中。但是,任何像这样的软件需求都会影响其他需求。喜欢报道。如果兽医对老板采用的狗的担忧,迟早他会问一个由谁采纳的狗的报告。就像句子一样,(老板采用的狗)如果狗类包含一系列老板类,那就更好了。

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

答案 11 :(得分:-1)

不确定你要求的是什么。但这是你想要的表结构:

狗桌

DOG_ID int PK DOG_Name varchar(50)

DogsPerBoss

ID int DOG_ID int BOSS_ID int DogNickName varchar(15)

老板

BOSS_ID int PK BOSS_Name varchar(50)