Objects / Entities: Many to Many + Many to One
Users / \ / \ M-T-O M-T-O / \ / \ Products----M-T-M----Tags
I wonder if there is any documentation on how to create a schema like this with entities. I got stuck at wondering which entity should be responsible for what in the re开发者_StackOverflow社区lation.
For example:
Lets say I want to add a tag to a product. I have a method like this in my
product entity:
public virtual void AddTag(Tag tag)
{
this.Tags.Add(tag); // IList<Tag> Tags
tag.AddProduct(this);
}
First this adds a the tag object to the list of Tags. Then that tag object adds 'this' product to it's own list of products.
So far so good.
But what if I want to add a product to a tag. I have a method like this in my
tag entity:
public virtual void AddProduct(Product product)
{
this.Products.Add(product); // IList<Product> Products
// product.AddTag(this);
}
So first I add the product object to a list of products in my tag object. I could then add 'this' tag to the Product but this is where I got stuck. The method that is commented throws a stackoverflow error because it calls back to AddProduct which calls AddTag and so on and so on.
Not sure if my schema is really correct either. The M-T-O from user to tags is there to make it easy when I want to see what tags a user has.
So I was wondering if anybody could point me into the right direction?
Thanks in advance,
Pickels
It makes a lot more sense to me to add a Tag to a Product. I would not allow a Product to be added to a Tag.
With many-to-many relationships you need to decide which entity is the primary entity in the relationship and control access to the collection through it. You can control access by marking the Add method on the other entity as internal.
Tag entity:
internal virtual void AddProduct(Product product)
{
this.Products.Add(product);
}
Do not use MTM relationship man, instead of this, you should create a new table Products_Tags with only 2 columns: ProductID and TagID. both foreign keys like this:
Users
/ \
/ \
M-T-O M-T-O
/ \
/ \
Products Tags
\ /
\ /
\ /
\ /
\Products_Tags/
There is a good practice to check if list already contains this element.
if ( !this.Tags.Contains(tag) )
{
this.Tags.Add(tag);
product.AddTag(this);
}
Tag.AddProduct should only add to the tag's internal list of products, and Product.AddTag should only add to the Product's internal list of tags. The persisting to the DB should handle the cross-mapping - when you save each product, the mapping table should have a row saved in the mapping table, and when you save each tag the same.
Maintaining this logic in the domain doesn't make sense to me, nor can I see any real benefits. A product has many tags, and a tag has many products - this should be how the domain is structured, with the persistence layer taking care of the many-to-many relationships.
This way, for any product you know what tags it has and vice versa, which is all you really need to know.
class Users
{
private Tags tags;
private Products products;
}
class Tags : IList<Tag> {}
class Tag
{
private Products products;
public void AddProduct(Product product);
}
class Products : IList<Product> {}
class Product
{
private Tags tags;
public void AddTag(Tag tag);
}
精彩评论