简体   繁体   中英

Avoid circular reference in domain model

this must be such a common scenario that there's been a lot written about it already, hopefully even a really good pattern. I have a domain model in which a custom container contains entities. For example (properties and interfaces excluded for brevity):

class Entity
{
    public int Id;
    public EntityContainer ParentContainer;
}


class EntityContainer
{
    public int Id;
    public IList<Entity> Entities = new List<Entity>();

    public void AddEntity(Entity entity)
    {
        entity.ParentContainer = this;
        Entities.Add(entity);
    }
}


class Main
{
    public Main()
    {
        Entity entity1 = new Entity();
        Entity entity2 = new Entity();
        EntityContainer entityContainer = new EntityContainer();
        entityContainer.AddEntity(entity1);
        entityContainer.AddEntity(entity2);

        // Can now traverse graph easily, e.g.
        Console.WriteLine("entity1's parent container ID = " + entity1.ParentContainer.Id);
        Console.WriteLine("Container contains at least this entity ID: " + entityContainer.Entities[0].Id);

    }
}

I can now easily traverse my object graph both ways, but have created a circular reference. Would you create a third type to divorce the dependencies?

Thanks in advance

There's nothing wrong with circular references, per se, and they are used extensively in the .NET Framework, eg XmlNode.OwnerDocument, Control.Parent.

If you need to traverse up the tree, then a back reference is fine to use.

In COM, circular references are tricky because if you were the set the container and all its children to nothing, then objects will not be cleaned up properly as the children still hold references to the parent. However the .NET garbage collection has no problem with this the way it is implemented.

Does the container need to know about the type of the contents? If not, generics can avoid this - ie Container<T> , where you happen to use Container<Entity> . Other than that; pushing the necessary details into an interface (or base-class) in an assembly that both can reference is a common approach.

Personally, I'd try to simply avoid the need for the child to know about the parent.

Also; note that if you do go down the abstraction (interface etc) route; this can have a big implication if you are using (for example) xml serialization.


(edit re comments) OK; first: what problem is the circular reference (within an assembly) causing; if none, leave it alone. If there is a problem, then you'll need an extra type; presumably some interfaces to represent the concrete types - ie where Entity : IEntity , and EntityContainer only knows about IEntity (or vv with IEntityContainer , or both),

Using circular referenced objects is ok in my book providing you are using some kind of lazy-loading pattern when designing those objects.

eg

You want to access: Company.Employee And in another scenario: Employee.Company

Which makes a circular reference ie Company.Employee.Company.Employee etc.

If these properties are not lazy-loaded eg A Company object always loads its Employee property and the Employee object always loads its Company property then it aint gonna work too well when you introduce a data source.

As so I don't see a problem with your class model but you could easily make a clean cut. Make Entity implement an interface IEntity and make EntityContainer hold a IList and unless you have a very specific reason for using IList you should considere IEnumerable, It would make it easy for the consumer of the EntityClass you use. Since passing any array of IEntity, or linq expression selecting IEntities would be possible

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM