2009-05-28 13 views
7

questo deve essere uno scenario talmente comune che è già stato scritto molto, speriamo anche in uno schema davvero valido. Ho un modello di dominio in cui un contenitore personalizzato contiene entità. Per esempio (proprietà e interfacce esclusi per brevità):Evita il riferimento circolare nel modello di dominio

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

    } 
} 

posso ora facilmente attraversare mio oggetto grafico entrambi i modi, ma hanno creato un riferimento circolare. Vuoi creare un terzo tipo per divorziare dalle dipendenze?

Grazie in anticipo

+2

Il modello in uso non consente una relazione inversa per più di un contenitore padre, quindi molto probabilmente non funzionerà allo stesso modo se si dispone di un'entità in più contenitori. – workmad3

+0

puoi chiarire come è circolare? stai creando una struttura ad albero per quanto ne so, quindi non vedo da dove proviene la circolarità da – RobV

+0

Entity ha un riferimento a EntityContainer e EntityContainer ha un riferimento a Entity. – ng5000

risposta

4

Non c'è niente di sbagliato con i riferimenti circolari, di per sé, e sono ampiamente utilizzati nella .NET Framework, ad es XmlNode.OwnerDocument, Control.Parent.

Se è necessario attraversare l'albero, è consigliabile utilizzare un riferimento posteriore.

In COM, i riferimenti circolari sono complicati perché se si imposta il contenitore e tutti i suoi figli su nulla, gli oggetti non verranno eliminati correttamente poiché i bambini conservano ancora i riferimenti al genitore. Tuttavia, la garbage collection di .NET non ha alcun problema con il modo in cui è implementata.

+0

Un problema che ho riscontrato con i riferimenti circolari è con la serializzazione. Generalmente, se l'oggetto A ha B e l'oggetto B ha A, non verrà serializzato. Questo è particolarmente problematico quando si mettono questi oggetti in stato di sessione out-of-proc. –

2

Does il contenitore bisogno di conoscere il tipo dei contenuti? In caso contrario, i farmaci generici possono evitare questo, ad esempio Container<T>, dove succede per utilizzare Container<Entity>. Oltre a quello; spingere i dettagli necessari in un'interfaccia (o classe base) in un assieme che entrambi possono fare riferimento è un approccio comune.

Personalmente, proverei semplicemente a evitare che il bambino sappia del genitore.

Inoltre; si noti che se si do si scende alla rotta di astrazione (interfaccia ecc.); questo può avere una grande implicazione se si utilizza (per esempio) la serializzazione xml.


(modifica commenti) OK; primo: quale problema è il riferimento circolare (all'interno di un assieme) che causa; se nessuno, lascia stare. Se c'è un problema, allora avrai bisogno di un tipo extra; presumibilmente alcune interfacce per rappresentare i tipi concreti - cioè dove Entity : IEntity e EntityContainer conosce solo IEntity (o vv con IEntityContainer, o entrambi),

+1

Il bambino deve conoscere il genitore per motivi di prestazioni. – ng5000

+0

Generica non sarebbe adatto per il dominio del problema reale che sto modellando. Questi sono tipi e relazioni specifici. – ng5000

+0

Grazie per l'aggiornamento - aggiungerò le interfacce. Penso che il consenso generale sul fatto che non ci sia nulla di sbagliato nei riferimenti circolari è il principale argomento da tenere in considerazione. – ng5000

1

Come così non vedo un problema con il modello di classe, ma si potrebbe facilmente fare un taglio pulito. Fai in modo che Entity implementa un'interfaccia IEntity e fai in modo che EntityContainer tenga un IList e, a meno che tu non abbia una ragione molto specifica per usare IList, dovresti avere IEnumerable, renderebbe facile per il consumatore l'EntityClass che usi. Dal momento che passa qualsiasi array di IEntity, o l'espressione linq selezionando IEntities sarebbe possibile

+0

Chiamare Enumerable.ToList() su un elenco non è assolutamente gratuito, poiché produce sempre un nuovo elenco, quindi dovrà fare una copia, che è O (N). –

+0

@Pavel nope È ottimizzato per restituire semplicemente l'oggetto elenco se è chiamato su un elenco –

+0

Hai torto. Apri crack System.Core.dll con ILSpy e guarda l'implementazione di 'Enumerable.ToList'. È letteralmente semplice come 'return new List()' più un argomento null check. Questo è abbastanza intenzionale: i progettisti di LINQ non volevano che i client di libreria fossero in grado di eseguire il downcast degli oggetti su qualcosa che l'autore della libreria non si aspettava che li facessero e potenzialmente di modificare i dati interni. Quindi tutti i metodi 'To ...()' creeranno una copia anche se l'originale è già di quel tipo. Al contrario, 'As ...()' restituisce l'oggetto originale. –

1

L'utilizzo di oggetti di riferimento circolari è ok nel mio libro, purché si stia utilizzando una sorta di modello a caricamento lento durante la progettazione di tali oggetti.

ad es.

si desidera accedere: Company.Employee E in un altro scenario: dipendenti.Società

che rende un riferimento circolare cioè Company.Employee.Company.Employee ecc

Se queste proprietà non sono pigri-caricato esempio Un oggetto Company carica sempre la proprietà Employee e l'oggetto Employee carica sempre la proprietà Company, quindi non funzionerà troppo quando si introduce un'origine dati.

Problemi correlati