简体   繁体   English

避免在域模型中使用循环引用

[英]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. 循环引用本身没有任何问题,它们在.NET Framework中被广泛使用,例如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. 在COM中,循环引用很棘手,因为如果您将容器及其所有子项设置为空,那么对象将无法正确清理,因为子项仍保留对父项的引用。 However the .NET garbage collection has no problem with this the way it is implemented. 但是,.NET垃圾收集对它的实现方式没有任何问题。

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> . 如果没有,泛型可以避免这种情况 - 即Container<T> ,您碰巧使用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. 如果您正在使用(例如)xml序列化,这可能会产生很大的影响。


(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), 可能是一些表示具体类型的接口 - 即Entity : IEntityEntityContainer只知道IEntity (或者使用IEntityContainer或者两者兼有),

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 您想要访问: Company.Employee在另一个场景中: Employee.Company

Which makes a circular reference ie Company.Employee.Company.Employee etc. 这是一个循环引用,即Company.Employee.Company.Employee等。

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. 如果这些属性不是延迟加载的,例如,公司对象总是加载其Employee属性,而Employee对象总是加载其Company属性,那么当您引入数据源时它将无法正常工作。

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. 使实体实现一个接口IEntity并使EntityContainer保持一个IList,除非你有一个非常具体的理由使用IList,你应该考虑IEnumerable,这将使你使用的EntityClass的使用者容易。 Since passing any array of IEntity, or linq expression selecting IEntities would be possible 由于传递任何IEntity数组,或者linq表达式选择IEntities是可能的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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