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

发布于 2024-07-26 18:05:19 字数 422 浏览 8 评论 0原文

假设我的数据库中有 2 个表。 例如:狗和 老板 这是多对多的关系,因为一个老板可以拥有不止 1 只狗,而一只狗可以有不止 1 个主人。 我是鲍比的主人,我的妻子也是。

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

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

班级老板可以拥有一批狗。 狗类可以拥有一系列 Boss。 --> 至少,我是这么认为的。 也许有更好的解决方案?

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

我希望我的问题有点清楚? 是否有实现这一目标的最佳方法的最佳实践? 你能给我一些参考吗?

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

Suppose I have 2 tables in a database.
eg: Dog & Boss
This is a many to many relationship, cause a boss can have more than 1 dog, and a dog can have more than 1 owner. I am the owner of Bobby, but so is my wife.

But many to many is not allowed, so there is a helpertable: DogsPerBoss

How to model this in code?

Class Boss can have a collection of Dogs.
Class Dog can have a collection of Bosses.
--> at least, that is what I think. Perhaps there are better solutions?

How about extra data that is in the helper-table?
Should that be in de Boss-class or in the Dog-class?
eg: Nickname
(I call the dog "good boy" and my wife calls him "doggie")

I hope my question is kinda clear?
Are there any best-practices on what is the best way to achieve this?
Can you give me some references?

An ORM (like NHibernate) is not an option.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

执笔绘流年 2024-08-02 18:05:19

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

对于对象模型,Dog 没有理由不能拥有 List 并且所有者也可以拥有 List。 仅当关系上有属性时,才需要中间类(UML 称为关联类)。 这时您将拥有一个具有额外属性的 DogOwnership 类,并且每个 Owner 都会有一个 List,每个 Dog 也是如此。 DogOwner 将有一只狗、一个主人和额外的财产。

Why are you talking about tables? Are you creating an object model or a database model?

For an object model, there's no reason a Dog can't have a List<Owner> and an owner have a List<Dog>. Only if you have attributes on the relationship do you need an intermediate class (what UML calls an Association Class). That's when you'd have a DogOwnership class with extra properties, and each Owner would have a List<DogOwnership>, and so would each Dog. The DogOwner would have a Dog, an Owner, and the extra properties.

人间☆小暴躁 2024-08-02 18:05:19
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); } }
}

上面维护了 Boss 可以有多个狗(有限制)和狗有多个 Boss 的关系。 这也意味着当老板添加狗时,他们可以为狗指定一个昵称,该昵称仅对该老板而言是唯一的。 这意味着其他老板可以添加同一只狗,但昵称不同。

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

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.

您显然可以根据需要调整它,但是,我认为您正在寻找的基本原理就在那里。

  • 老板可以拥有多只狗,但有限制
  • 狗可以有多个老板
  • 老板可以对同一条狗有不同的昵称。
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); } }
}

The above maintains the relationship of Bosses can have multiple dogs (with a limit applied) and dogs having multiple bosses. It also means that when a boss is adding a dog, they can specify a nickname for the dog which is unique to that boss only. Which means other bosses can add the same dog, but with different nicknames.

As for the limit, I would probably have this as an App.Config value which you just read in before instantiating the boss object(s). So a small example would be:

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.

You can obviously tweak this as you see fit, however, I think the fundamentals of what you are looking for are there.

  • Boss can have multiple dogs with limit
  • Dogs can have multiple bosses
  • Bosses can have different nicknames for same dog.
梦里梦着梦中梦 2024-08-02 18:05:19

像这样的东西;
不过,它仍然需要一些微调(将集合设为私有并为其添加一个只读公共访问器,例如返回一个只读集合,但您会明白其中的含义。

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);
        }
    }
}

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

Something like this;
It still needs some finetuning though (make the collection private and add a readonly public accessor for it which returns a readonlycollection for instance, but you'll catch the drift.

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);
        }
    }
}

In this way, you could model a many-to-many in your code where every Dog knows his Bosses, and every Boss knows his Dogs.
When you need extra data in the helper table, you'll need to create another class as well.

几味少女 2024-08-02 18:05:19

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

因为确实有包含独特信息的字段,所以我倾向于不再将这些关系视为多对多。

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

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

然而,无论你给狗起一个名字来称呼它还是拥有它,都是独立的。

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

the traditional many to many relation would have no extra fields on the matching table.

Because you do have fields with unique information I tend to stop thinking of these relations as many to many.

Once you add information to the matching table i think you have then made this table into an entity in its own right and so needs its own object to represent it.

At this point you can begin to have a DogsName class to connect a person and a dog - both of which would contain references to this object as part of a collection.

However whether you give the dog a name to be called by or own the dog are independant.

As well as modelling the relation of dogs name according to different people you also need to model the ownership relationships. In memory this would mean both objects contain a list of the other objects.

椵侞 2024-08-02 18:05:19

这是数据库和对象世界之间的一个典型问题,多对多不起作用,因此你的帮助表和多对多工作正常的对象世界。 一旦关系具有属性,您就应该创建一个新类来保存该信息。 然而,如果您查看对象关系映射(ORM),您将为自己节省大量时间,整个领域的发展就是为了解决数据库和对象之间的这个(以及许多其他)问题。

This is a classic issue between databases where many to many doesn't work, hence your helper table, and the object world where many to many works fine. As soon as the relationship has attributes then you should create a new class to hold that information. However, you'll save yourself a lot of head time if you look at Object Relation Mapping - ORM - that whole field grew up to solve this (and many other) problems between DB and Object.

娜些时光,永不杰束 2024-08-02 18:05:19

如果您有一个简单的多对多链接表,其中包含关系中每个表的外键,那么您将按照您的建议对其进行建模:Boss 有一个 Dogs 集合,而 Dog 有一个 Bosses 集合。

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

If you have a simple many-to-many linking table with foreign keys from each table in the relationship, then you would model it as you suggest: Boss has a collection of Dogs and Dog has a collection of Bosses.

If you have a many-to-many relationship with extra data, such as Nickname, then you would model that as two one-to-many relationships. Create an entity, such as DogBoss so that Boss has a collection of DogBoss and Dog has a collection of DogBoss.

蝶舞 2024-08-02 18:05:19

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

List<Bosses> BossList;

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

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

Am I missing something or is the only code you need for this as follows:

List<Bosses> BossList;

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

You don't need to explicitly model the two-way relationship. It's implicit in the code structure. There may be other reasons to do so, but in general it is sufficient to have a one-way reference and a way to traverse the set of referencing objects.

清醇 2024-08-02 18:05:19

如果您不需要记录昵称,那么 Dog 应该有 Bosses 列表,Boss 应该有 Dogs 列表。

如果 Dog 和 Boss 之间的关系具有属性(在本例中为昵称),那么您应该创建一个类来表示该关系,并让 Dog 和 Boss 都保存该类型的列表。

我已经使用 NHibernate 一段时间了,发现它对于缓解这种非常有用="http://en.wikipedia.org/wiki/Object-relational_impedance_mismatch" rel="nofollow noreferrer">对象关系阻抗不匹配

If you didn't need to record the nickname, then Dog should have a list of Bosses and Boss should have a list of Dogs.

If the relationship between Dog and Boss has attributes, in this case nickname, then you should create a class to represent that relationship and have Dog and Boss both hold lists of that type.

I've been using NHibernate for a while now and find it very useful for easing this sort of object relational impedance mismatch.

等风来 2024-08-02 18:05:19

我想缺少一些东西。 为什么不允许多对多?

public class Boss
{
    Dog[] dogs;
}

public class Dog
{
    Boss[] bosses;
}

I guess am missing something. Why is many to many not allowed?

public class Boss
{
    Dog[] dogs;
}

public class Dog
{
    Boss[] bosses;
}
许仙没带伞 2024-08-02 18:05:19

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

一张表用于 DOGS,一张表用于 BOSSES(每个表都有一个唯一的键),第三个表通常是“连接表”。

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

现在,当以更面向对象的方式在代码中对此进行建模时,通常通过使用 Dog 类和 Boss 类来实现。
除了这些对象中的每一个都具有通常的原子属性之外,每个对象还将
还公开一个属性,该属性是另一个属性的集合。

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

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

In a relational model, the best way to model a many to many relationship (using your example of Dogs/Bosses) is to have three separate tables.

One table for DOGS, one table for BOSSES (and each of these tables has a unique key), and the third table is usually a "junction table".

This table will usually have at least two fields, one field for the foreign key for a Dog and the other field for the foreign key of a Boss.
This way each Dog can have many bosses, and each Boss can have many Dogs.

Now, when it come to modeling this in code in a more object-oriented manner, this is usually achieved by having a Dog class and a Boss class.
As well as having the usual atomic properties for each of these objects, each one would
also expose a property that is a collection of the other.

So, for example, a Dog object would have a property called "Bosses". This property would expose a collection of Boss objects that are allocated to the specific Dog object (as defined in the junction table), and on the other side, each Boss object would expose a property called Dogs which would be a collection of Dog objects allocated to that specific Boss object (as defined by the junction table).

Note that there may well be some "overlap" in these objects (i.e. one "dog" object may have "boss" objects that another "dog" object has), however, this is the traditional mechanism for translating a three-table many-to-many relational model into an object oriented one.

江心雾 2024-08-02 18:05:19

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

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

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

  • 如果您正在开发该软件,并且您是一名不治疗流浪狗的兽医,那么患者 - 家长关系需要是这样的:
    一只狗必须至少有一个老板,老板也必须至少有一只狗,我们需要考虑这种特殊的关系情况。 这意味着这些类实例中的任何一个都不能在没有彼此的情况下创建。 所以我们需要在面向对象设计中定义这个专业。 这意味着我们需要一个代表这种依赖关系的类。 当然,这种依赖关系将存储在联结表中。

-如果你的软件是为治疗流浪狗的兽医开发的,而这些狗被老板收养,你的设计应该是这样的:
任何狗都可能没有老板,并且任何老板在收养之前都可能没有任何狗。 在这种情况下,我们的面向对象设计需要关心这种特殊情况。 这个案例与第一个案例有点相似。 因此我们可以将任何类的集合添加到其他类中。 但是,任何这样的软件需求都会影响其他需求。 就像报道一样。 如果兽医担心老板收养的狗,他迟早会要求报告哪只狗被谁收养。 就像句子中的那样,(老板收养的狗)如果狗类包含老板类的集合,那就更好了。

我希望我能够对你的问题做出正确的回答。

Every time we need to think about real life and our needs. In this case, the key point is which one should have another one.

In real life a dog and a boss may dont have each other. But your software needs should effect this relationship.

  • For example if you are developing a veterinarian patient management software, for a veterinarian who cures stray dogs then Patient(dog)-Guardian(boss) relationship should be like this :
    Bosses must have at least one dog and dog may dont have any boss(then boss id is the foreign key of this relationship) that means in your design the dog class must hold a collection of bosses. Why because any boss instance can not be created without any dog(s). We can get this decision with the the data logic too. Lets think about when you are trying to save your dog and boss classes in database. If the relation condition like above, while saving a boss you should insert a junction record into the junction table.

  • If you are developing that software a veterinarian who dont cures stray dogs, then Patient - Parent relationship needs to be like this:
    A dog must have at least one boss and boss must have at least a dog, and we need to consider this special relationship case. That means any of these classes instances can not be created without eachother. So we need to define this speciality in our OO design. That means we need a class which represents this dependency. Ofcourse this dependency will stored in junction table.

-If your software developed for a veterinarian who cures stray dogs and these dogs adopted by bosses your design should be like this:
Any dog may dont have boss(es) and any boss may dont have any dog(s) until adoption. In this case our OO design needs to care about this special case. This case a little bit same as the first one. So we can add collection of any class into other one. But, any software needs like this will effect other needs. Like reporting. If veterinarian concerns about dogs which adopted by boss(es), sooner or later he asks a report which dog adopted by who. Like in the sentence, (dogs adopted by boss(es)) it is better if dog class contains a collection of boss classes.

I hope I can give proper answers to your question.

望笑 2024-08-02 18:05:19

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

Dog Table

DOG_ID int PK
DOG_Name varchar(50)

DogsPerBoss

ID int
DOG_ID 整数
BOSS_ID 整数
DogNickName varchar(15)

Boss

BOSS_ID int PK
BOSS_名称 varchar(50)

Not sure on what your asking for. But this is the table structure you want:

Dog Table

DOG_ID int PK
DOG_Name varchar(50)

DogsPerBoss

ID int
DOG_ID int
BOSS_ID int
DogNickName varchar(15)

Boss

BOSS_ID int PK
BOSS_Name varchar(50)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文