Maintaining a two way relationship between classes

前端 未结 6 609
萌比男神i
萌比男神i 2021-02-02 00:34

It is pretty common, especially in applications with an ORM, to have a two way mapping between classes. Like this:

public class Product
{
    private List

        
6条回答
  •  遇见更好的自我
    2021-02-02 01:28

    First, I think the example your present is confusing - it's uncommon for something like a Price to be modeled as an object or to have reference to the entities that would have a price. But I think the question is legitimate - in the ORM world this is sometimes referred to as graph consistency. To my knowledge there isn't one definitive way to tackle this problem, there are several ways.

    Let's start by changing the example slightly:

    public class Product
    {
        private Manufacturer Manufacturer { get; private set; }
    }
    
    public class Manufacturer
    {
        private List Products { get; set; }
    }
    

    So every Product has one Manufacturer, and each Manufacturer could have a list of products. The challenge with the model is that if the Product class and Manufacturer class maintain disconnected references to one another, updating one can invalidate the other.

    There are several ways to address this issue:

    1. Eliminate the circular reference. This solves the problem but makes the object model less expressive and harder to use.

    2. Change the code so that the Manufacturer reference in Product and Products list in Manufacturer are reflexive. In other words, changing one affects the other. This generally requires some code the setter and the collection to intercept changes and reflect them into one another.

    3. Manage one property in terms of the other. So, rather than storing a reference to a manufacturer within Product, you compute it by search through all Manufacturers until you find the one that owns you. Conversely, you could keep a reference to the Manufacturer in the Product class and build the list of Products dynamically. In this approach, you would generally make one side of the relationship read-only. This, by the way, is the standard relational database approach - entities refer to each other through a foreign key which is managed in one place.

    4. Externalize the relationship from both classes and manage it in a separate object (often called a data context in ORM). When Product wants to return its manufacturer it asks the DataContext. When the Manufacturer want to return a list of Products it does the same. Internally, there are many ways to implement a data context, a set of bi-directional dictionaries is not uncommon.

    Finally, I will mention, that you should consider using an ORM tool (like NHibernate or CSLA) that can help you manage graph consistency. This is generally not an easy problem to solve correctly - and it can easily become very complicated once you start exploring cases like many-to-many relationships, one-to-one relationships, and lazy loading of objects. You are better of using an existing library or product, rather than inventing a mechanism of your own.

    Here are some links that talk about bidirectional associations in NHibernate that you may find useful.

    Here's a code example of managing the relationships directly yourself using method #2 - which is typically the simplest. Note that only one side of the relationship is editable (in this case, the Manufacturer) - external consumers cannot directly set the Manufacturer of a Product.

    public class Product
    {
        private Manufacturer m_manufacturer;
    
        internal Manufacturer Manufacturer
        {
            get { return m_manufacturer; }
            set { m_manufacturer = value; }
        }
    }
    
    public class Manufacturer
    {
        private List m_Products = new List();
    
        public IEnumerable Products { get { return m_Products.AsReadOnly(); } }
    
        public void AddProduct( Product p )
        {
            if( !m_Products.Contains( p ) )
            {
                m_Products.Add( p );
                p.Manufacturer = this;
            }
        }
    
        public void RemoveProduct( Product p )
        {
            m_Products.Remove( p );
            p.Manufacturer = null;
        }
    }
    

提交回复
热议问题