2009-06-03 9 views
11

Quindi, in un modello tipico in cui un genitore può avere molti figli e un bambino che può avere solo un genitore, come si gestisce l'aggiunta di figli. Ho usato questo approccio;Best practice per la gestione delle raccolte genitore-figlio di NHibernate

public class Parent 
{ 
    public Parent() 
    { 
     Children = new List<Child>(); 
    } 

    public IList<Child> Children 
    { 
     get; 
     private set; 
    } 
} 

public class Child 
{ 
    public Parent Parent 
    { 
     get; 
     set; 
    } 
} 

var child = new Child(); 
var parent = new Parent(); 
parent.Children.Add(child); 
child.Parent = parent; 

Il problema è che ovunque voglio aggiungere un nuovo figlio che ho avuto modo di ricordare di aggiungere un riferimento sia per il bambino e la madre e la sua un po 'di dolore. Potrei semplicemente aggiungere un metodo AddChild alla classe genitore e renderlo responsabile per l'aggiunta di bambini - il problema ora è che ci sono 2 modi per aggiungere un bambino, attraverso la proprietà Children e il metodo. Quindi questa è una soluzione migliore?

public class Parent 
{ 
    public Parent() 
    { 
     children = new List<Child>(); 
    } 

    private IList<Child> children 
    { 
     get; 
     private set; 
    } 

    public IEnumerable<Child> Children 
    { 
     get 
     { 
      return children; 
     } 
    } 

    public void AddChild(Child child) 
    { 
     children.Add(child); 
     child.Parent = this; 
    } 
} 

Esistono guidences per le migliori pratiche su questo, e cosa fai?

risposta

2

lo faccio così, tranne che io non uso di proprietà per la lista privata

private IList<Child> _children 
public IEnumerable<Child> Children 
{ 
    get 
    { 
     return children; 
    } 
} 
+2

questa è una buona soluzione ma con un inconveniente che se l'utente ha lanciato i figli su IList , avrebbe accesso all'elenco e può modificarlo .. in altro modo potrebbe utilizzare ReadOnlyCollection <> –

10

Questo non è un problema di NHibernate.

È necessario implementare il metodo AddChild. Le classi sono responsabili della loro coerenza, quindi non dovrebbero esporre nulla che non dovrebbe essere disponibile. Ad esempio, l'elenco (mutabile) dei bambini dovrebbe essere nascosto. Esporre un oggetto IEnumerable è una buona idea.

Il tuo secondo codice è un buon punto di partenza. Probabilmente hai bisogno di altri metodi, come RemoveChild o CoundChildren.

+0

Parlando del secondo esempio, la mia unica preoccupazione è come questo possa funzionare con Linq su NHibernate. Poiché la proprietà mappata effettiva è ora privata, suppongo che non sarebbe visibile. qualche idea? – Gareth

+0

È possibile specificare nel proprio mapping che NHibernte debba utilizzare il campo anziché la proprietà quando si imposta un valore. (Date un'occhiata all'attributo di accesso, ad esempio). –

+0

Sì, quello che stavo ottenendo era che quando Linq su NHibernate è finito come si può usare se il campo è privato e l'accesso pubblico restituisce solo un riferimento alla collezione originale. Per esempio. session.Linq () .Where (x => x.Children.Name.Equals ("Jonnie")) – Gareth

5

lo faccio come questo:

public class Parent 
{ 
    private ISet<Child> _children = new HashedSet<Child>(); 

    public ReadOnlyCollection<Child> Children 
    { 
     get{ return new List(_children).AsReadOnly(); } 
    } 

    public void AddChild(Child c) 
    { 
     if(c != null && !_children.Contains (d)) 
     { 
      c.Parent = this; 
      _children.Add (c); 
     } 
    } 
} 

Quindi, in realtà, questo è un po 'quello che Stefan dice pure. Ho appena esposto una copia di sola lettura dell'elenco dei bambini, in modo da poter facilmente scorrere i figli di un genitore e ottenere il numero di figli che il genitore ha. L'aggiunta e la rimozione di figli al genitore deve essere eseguita utilizzando i metodi AddChild & RemoveChild.

+1

Un set non aggiungerà duplicati quindi non c'è bisogno di controllare se il bambino esiste già. –

-1

Non mi piacciono tutti i metodi aggiuntivi AddXXX() e RemoveXXX() che ingombrano le mie interfacce di entità. Invece, ho un elenco personalizzato che solleva eventi quando vengono chiamati i metodi Add() e Remove().

Il collegamento avviene poi nei gestori di eventi:

public class Course() 
{ 
    public Course() 
    { 
    this.Topics = new EntityList<Topic>(); 
    this.Topics.AddItem += new AddItemEventHandler<Topic>(Topic_AddItem); 
    this.Topics.RemoveItem += new RemoveItemEventHandler<Topic>(Topic_RemoveItem); 
    } 

    public EntityList<Topic> Topics { get; private set; } 

    private void Topic_AddItem(Topic item, object args) 
    { 
    // Replace with your linking code: 
    EntityLinker.Link(this).With(item, args); 
    } 

    private void Topic_RemoveItem(Topic item, object args) 
    { 
    // Replace with your unlinking code: 
    EntityLinker.Unlink(this).From(item, args); 
    } 
} 
+4

Non mi piace questo approccio. :) C'è molta "magia" in scena dietro le quinte. Ricordo che Ayende usò una volta questo approccio (nei generici di NHibernate se non sbaglio), ma ora non lo vedo più usando questo approccio. Ho appena scoperto che la logica del 'link' è troppo nascosta. Accanto a ciò, non vedo l'aggiunta di metodi Addxxxx alle mie entità come ingombrante dell'interfaccia. Inoltre, lo trovo migliore poiché è più esplicito. –

+0

Questo non sembra molto leggibile per me. – UpTheCreek

1

sto usando IEnumerable pubblico Aggiungi | Rimuovi metodo metodi.

Tuttavia non mi piace molto, perché non è intuitivo e definisce la definizione della classe.

Mi chiedo perché le persone non usano CustomCollection dove sostituiscono le funzioni Aggiungi, Rimuovi, Sostituisci ?? (come è fatto ovunque nel codice MS) ????

0

Supporto la soluzione accettata a questa domanda, tuttavia la soluzione presentata non è completa, poiché l'utilizzo di campi privati ​​richiede qualche configurazione aggiuntiva nei mapper. Per il bene degli altri, quanto segue è la soluzione completa:

public partial class Test 
{ 
    private readonly IList<Child> children = new List<Child>(); 
    public virtual IEnumerable<Child> Children 
    { 
     get 
     { 
      return children; 
     } 
    } 
} 

Nota che la collezione esposta al pubblico deve essere virtuale per NHibernate usarlo.Mi piace anche renderlo un campo readonly che viene inizializzato quando viene creata la classe per assicurarsi che esista in tutti gli scenari. Ecco il mapper associato:

proprietà
public class TestMap : ClassMap<Test> 
{ 
    ... 
    HasMany(s => s.Children).Access.CamelCaseField(); 
} 

L'accesso dice NHibernate utilizzare il campo privato per la mappatura dei valori nel modello. Ci sono anche altre opzioni sulla proprietà Access che consentono l'utilizzo di varie configurazioni di denominazione.

0

Se si dispone di una chiave esterna nel DB e si utilizza Identity (SQL Server) per generare le chiavi primarie, si sta andando a BISOGNO il backlink dal figlio al genitore. Altrimenti, l'inserto si lamenterà per il bambino, perché Nhibernate ha bisogno di fare un po 'avanti e indietro per l'ID genitore, ma non l'ha ancora fatto.

Cosa abbiamo finito per sbarazzarci dei collegamenti a ritroso: utilizzare il generatore HiLo di NHibernate. In questo modo, NHibernate ha sempre gli ID di cui ha bisogno per inserire le relazioni genitore/figlio.

< 3!

Problemi correlati