2009-03-14 13 views
10

Il modello di dominio su cui sto lavorando ha aggregati radice e entità figlio. Qualcosa come il seguente codice:Qual è la best practice per gli elenchi di sola lettura in NHibernate

class Order 
{ 
    IList<OrderLine> Lines {get;set;} 
} 

class OrderLine 
{ 
} 

Ora voglio la mia fine di controllare le linee. Qualcosa di simile:

class Order 
{ 
    OrderLine[] Lines {get;} 

    void AddLine(OrderLine line); 
} 

In questo momento stiamo utilizzando il seguente schema:

class Order 
{ 
    private IList<OrderLine> lines = new List<OrderLine>(); 
    OrderLine[] Lines {get {return this.lines;}} 

    void AddLine(OrderLine line) 
    { 
     this.orders.Add(line); 
    { 
} 

NHibernate viene mappato direttamente al campo linee.

Ora domande ...

  • Cosa professi in tali situazioni?
  • Qualcuno usa i metodi: public IEnumerable GetLines()
  • Cosa si utilizza come tipo di ritorno per la proprietà? Può essere ReadOnlyCollection o IEnumerable;
  • Forse questo non è il posto migliore per chiedere? Suggerisci per favore

Aggiornamento: Sembra vittorie IEnumerable, tuttavia la soluzione ancora non è perfetta ...

risposta

9

Il modello che uso è:

class Order 
{ 
    private List<OrderLine> lines = new List<OrderLine>(); 

    IEnumerable<OrderLine> Lines { get { return this.lines; } } 

    void AddLine(OrderLine line) 
    { 
     this.orders.Add(line); 
    } 
} 

Se stai usando NET 3.5 si ottiene tutto la ricerca funzionalità che potresti desiderare per IEnumerable usando LINQ e nascondi l'implementazione della raccolta.

Il problema con il ritorno OrderLine [] è che la tua collezione può essere modificata esternamente per esempio:

Order.Lines[0] = new OrderLine(). 
+0

in materia di "Order.Lines [0] = new OrderLine();" non può. Ogni volta che accedi alle Linee, viene emessa una nuova copia dell'array (non mi piace però ...). IEnumerable ha la mancanza di funzionalità di elenco, intendo Count, accesso per indice. Naturalmente, LINQ copre questo. Grazie! –

+0

Qui è ancora possibile trasmettere la proprietà Lines a un elenco e modificare la raccolta in questo modo ... –

+2

Sì, ma è necessario eseguire il casting. Non si tratta tanto di protezione, ma di mostrare ciò che sei autorizzato a fare nella raccolta. Puoi restituire this.lines.AsReadonly() se desideri una maggiore sicurezza. – gcores

3

espongo collezioni ReadOnlyCollection e utilizzare metodi AddX e RemovEx per il mantenimento delle collezioni. Siamo passati a 3.5 e sto pensando di esporre invece IEnumerable. Nella maggior parte dei casi con NHibernate il bambino ha un riferimento al genitore in modo da esporre aggiungere e rimuovere i metodi consente di mantenere quel rapporto:

public void AddPlayer(Player player) 
    { 
     player.Company = this; 
     this._Players.Add(player); 
    } 

    public void RemovePlayer(Player player) 
    { 
     player.Company = null; 
     this._Players.Remove(player); 
    } 
+0

Yeh, sembra che IEnumerable batte ReadOnlyCollection ... ReadOnlyCollection rende l'API confusa. Ha ancora il metodo Add che genera eccezioni in runtime ... –

+1

Mike - Penso che ti sbagli su questo. Restituzione di una lista .AsReadOnly() espone un metodo Aggiungi ma ReadOnlyCollection no. ReadOnlyCollection è in System.Collections.ObjectModel quindi è spesso trascurato. –

+0

Divertente, sì è vero ..L'ho trascurato. Grazie mille. –

1

Se sto esponendo un elenco che non deve essere modificato, allora io uso IEnumerable e yield. Trovo ingombrante cercando di utilizzare ReadOnlyCollections in collaborazione con NHiberante.

Con questo approccio, si ha ancora il campo delle righe private che viene mappato e popolato tramite NHibernate; tuttavia, l'accesso pubblico alla raccolta viene eseguito tramite iteratori. Non è possibile aggiungere o rimuovere dall'elenco sottostante con questa proprietà Lines.

Ad esempio:

public IEnumerable<OrderLine> Lines { 
    get { 
     foreach (OrderLine aline in lines) { 
      yield return aline; 
     } 
    } 
} 
+0

Perché non solo "restituire le linee"? Stai proteggendo dal casting? La soluzione con yield indica che Count() richiederà foreach e caricherà tutte le ite backed (in caso di carico lento). –

+0

L'IEnumerable combinato con il rendimento consente di percorrere la raccolta senza poter aggiungere/rimuovere elementi. –

+0

Bene, IEnumerable tutto da solo potrebbe anche impedirti di aggiungere/rimuovere elementi, quindi un 'public IEnumerable Lines {get {return lines; }} dovrebbe essere sufficiente. – Dav

14

lo faccio in questo modo:

public class Order 
{ 
     private ISet<OrderLine> _orderLines = new HashedSet<OrderLine>(); 

     public ReadOnlyCollection<OrderLine> OrderLines 
     { 
      get { return new List<OrderLine>(_orderLines).AsReadOnly(); } 
     } 

     public void AddOrderLine(OrderLine ol) 
     { 
      ... 
     } 
} 

Poi, offcourse, nella mappatura, NHibernate è stato detto di utilizzare il campo _orderLines:

<set name="OrderLine" access="field.camelcase-underscore" ... > 
... 
</set> 
+0

Il codice che segue il codice non funzionerà: order.OrderLines! = Order.OrderLines; Non sono sicuro se questo sia cattivo o no ... Comunque grazie. –

+0

Questo non importa imho, dal momento che non si dovrebbe confrontare in questo modo :) –

+0

Buona soluzione. Questo mi ha aiutato molto. Grazie! – CalebHC

0

Ho trascorso diversi giorni alla ricerca di un approccio migliore per le liste readonly in NHibernate. Questa discussione mi ha aiutato molto a formare quella che si adatta al nostro progetto.

C'è un approccio ho iniziato ad usare:

  1. campi Backing vengono utilizzati per archiviare collezioni
  2. IEnumerable < T> viene utilizzato per esporre le collezioni per costringere i clienti utilizzare AddLine() e RemoveLine() metodi .
  3. Il tipo ReadOnlyCollection viene utilizzato in aggiunta a IEnumerable.

Codice:

public class Order 
{ 
    private readonly IList<OrderLine> lines = new List<OrderLine>(); 

    public virtual IEnumerable<OrderLine> Lines 
    { 
     get 
     { 
      return new ReadOnlyCollection<OrderLine>(lines); 
     } 
    } 

    public void AddLine(OrderLine line) 
    { 
     if (!lines.Contains(line)) 
     { 
      this.lines.Add(line); 
      line.Order = this; 
     } 
    } 

    public void RemoveLine(OrderLine line) 
    { 
     if (lines.Contains(line)) 
     { 
      this.lines.Remove(line); 
      line.Order = null; 
     } 
    } 
} 

public class OrderLine 
{ 
    public Order Order { get; set; } 
} 
Problemi correlati