2009-03-14 8 views
10

El modelo de dominio en el que estoy trabajando tiene entidades agregadas y secundarias de raíz. Algo así como el código siguiente:Cuál es la mejor práctica para las listas de solo lectura en NHibernate

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

class OrderLine 
{ 
} 

Ahora quiero mi fin de controlar las líneas. Algo así:

class Order 
{ 
    OrderLine[] Lines {get;} 

    void AddLine(OrderLine line); 
} 

En este momento estamos utilizando el siguiente patrón:

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

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

NHibernate se asigna directamente al campo líneas.

Ahora preguntas ...

  • ¿Qué es lo que practicas en tales situaciones?
  • ¿Alguien usa métodos: public IEnumerable GetLines()
  • ¿Qué está utilizando como tipo de devolución para la propiedad? Puede ser ReadOnlyCollection o IEnumerable;
  • ¿Puede ser que este no sea el mejor lugar para preguntar? Sugerir por favor.

Actualización: Parece victorias IEnumerable, sin embargo todavía no es la solución perfecta ...

Respuesta

9

El patrón de uso es:

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

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

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

Si está utilizando .NET 3.5 se obtiene toda la búsqueda funcionalidad que podría desear para IEnumerable utilizando LINQ, y oculta su implementación de colección.

El problema con la devolución de OrderLine [] es que su colección se puede modificar externamente por ejemplo:

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

con respecto a "Order.Lines [0] = nuevo OrderLine();" no puede. Cada vez que accede a las Líneas, se envía una nueva copia de la matriz (aunque esto no me gusta ...). IEnumerable tiene falta de funcionalidad de lista, es decir, Count, access by index. Por supuesto, LINQ cubre esto. ¡Gracias! –

+0

Aquí, aún puede convertir la propiedad Líneas en una Lista y modificar la colección de esa manera ... –

+2

Sí, pero tiene que hacer el casting. No se trata tanto de protección sino de mostrar lo que se le permite hacer a la colección. Puede devolver this.lines.AsReadonly() si desea esa seguridad adicional. – gcores

3

I exponga colecciones como ReadOnlyCollection y usar métodos ADDX y RemovEx para el mantenimiento de las colecciones. Acabamos de cambiar a 3.5 y estoy pensando en exponer IEnumerable en su lugar. En la mayoría de los casos con NHibernate el niño tiene una referencia a la matriz de modo exponiendo agregar y quitar métodos le permite mantener esa relación:

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

Sí, parece que IEnumerable beats ReadOnlyCollection ... ReadOnlyCollection hace que la API sea confusa. Todavía tiene el método Add que lanzará una excepción en tiempo de ejecución ... –

+1

Mike - Creo que te equivocas en esto. Devolver una lista .AsReadOnly() expone un método Add pero ReadOnlyCollection no lo hace. ReadOnlyCollection se encuentra en System.Collections.ObjectModel por lo que a menudo se pasa por alto. –

+0

Gracioso, sí, eso es cierto ...Lo pasé por alto. Muchas gracias. –

1

Si estoy exponiendo una lista que no debe ser modificado, entonces yo uso IEnumerable y rendimiento. Me resulta engorroso tratar de usar ReadOnlyCollections junto con NHiberante.

Con este enfoque, todavía tiene el campo de líneas privadas que se asigna y se llena a través de NHibernate; sin embargo, el acceso público a la colección se realiza a través de iteradores. No puede agregar o eliminar de la lista subyacente con esta propiedad Líneas.

Por ejemplo:

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

¿Por qué no simplemente "devolver líneas"? ¿Estás protegiéndote del lanzamiento? La solución con rendimiento significa que Count() requerirá foreach y cargará todos los ites respaldados (en caso de carga lenta). –

+0

El IEnumerable combinado con el rendimiento le permite recorrer la colección sin poder agregar/quitar elementos. –

+0

Bueno, IEnumerable por sí solo también le impediría agregar/eliminar elementos, por lo que un 'public IEnumerable Lines {get {return lines; }} 'debería ser suficiente. – Dav

14

lo hago de esta manera:

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) 
     { 
      ... 
     } 
} 

Entonces, offcourse, en el mapeo, NHibernate se le dice a utilizar el campo _orderLines:

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

Con su código, el código siguiente fallará: order.OrderLines! = Order.OrderLines; No estoy seguro de si esto es malo o no ... De todos modos, gracias. –

+0

Eso no importa, porque no debes compararlo :) –

+0

Buena solución. Esto me ayudó mucho. ¡Gracias! – CalebHC

0

He pasado varios días buscando el mejor enfoque para las listas de solo lectura en NHibernate. Esta discusión me ayudó mucho a formar la que se adapta a nuestro proyecto.

Hay un enfoque que empecé a utilizar:

  1. campos Copia de seguridad se utilizan para almacenar colecciones
  2. IEnumerable < T> se utiliza para exponer colecciones para forzar a los clientes utilizar AddLine() y RemoveLine() métodos .
  3. El tipo ReadOnlyCollection se usa además de IEnumerable.

Código:

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; } 
} 
Cuestiones relacionadas