2009-06-03 4 views
11

Entonces, en un modelo típico en el que tiene un padre que puede tener muchos hijos y un hijo que solo puede tener uno, ¿cómo gestiona la adición de hijos? He estado usando este enfoque;Práctica recomendada para el manejo de las colecciones padre e hijo 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; 

El problema es que en todas partes Quiero añadir un nuevo hijo que tengo que recordar que agregar una referencia tanto para el niño y el padre y es un poco de dolor. Solo podría agregar un método AddChild a la clase principal y hacer que el responsable de agregar hijos: el problema ahora es que hay 2 formas de agregar un elemento secundario a través de la propiedad y el método Children. Entonces, ¿esta es una mejor solución?

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

¿Hay alguna guía para las mejores prácticas en esto, y qué haces?

Respuesta

2

lo hago de esa manera, excepto que yo no uso la propiedad privada para la lista

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

esta es una buena solución, pero con el inconveniente de que si el consumidor emitiera Children to IList accedería a la lista y podría modificarla ... de otra manera podría estar utilizando ReadOnlyCollection <> –

10

Esto no es un problema de NHibernate en absoluto.

Debe implementar el método AddChild. Las clases son responsables de su coherencia, por lo que no deben exponer nada que no debería estar disponible. Por ejemplo, la lista de niños (mutable) debe estar oculta. Exponer un IEnumerable es una buena idea.

Su segundo código es un buen punto de partida. Probablemente necesites algunos métodos más, como RemoveChild o CoundChildren.

+0

Hablando del segundo ejemplo, mi única preocupación es cómo esto puede funcionar con Linq a NHibernate. Como la propiedad mapeada real ahora es privada, asumiría que no sería visible. ¿Alguna idea? – Gareth

+0

Puede especificar en su asignación que NHibernte debe usar el campo en lugar de la propiedad al establecer un valor. (Eche un vistazo al atributo de acceso, por ejemplo). –

+0

Sí, a lo que me refería era que cuando termine Linq a NHibernate, ¿cómo podría usarlo si el campo es privado y el acceso público solo devuelve una referencia a la colección original? P.ej. session.Linq () .Where (x => x.Children.Name.Equals ("Jonnie")) – Gareth

5

lo hago de esta manera:

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

Así que, de hecho, eso es un poco lo que dice Stefan también. Acabo de exponer una copia de solo lectura de la lista de niños, para que pueda iterar fácilmente sobre los hijos de un padre y obtener el número de hijos que tiene el padre. La adición y eliminación de elementos secundarios al elemento primario debe realizarse mediante AddChild & Métodos del miembro RemoveChild.

+1

Un conjunto no agregará duplicados entonces no hay necesidad de verificar si el niño ya existe. –

-1

No me gustan todos los métodos extra AddXXX() y RemoveXXX() que saturan las interfaces de mi entidad. En cambio, tengo una lista personalizada que genera eventos cuando se llaman los métodos Add() y Remove().

La vinculación a continuación, pasa en los controladores de eventos:

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

No me gusta este enfoque. :) Hay mucha 'magia' sucediendo detrás de escena. Recuerdo que Ayende una vez usó este enfoque también (en genéricos NHibernate si no me equivoco), pero ahora no lo veo usando este enfoque más. Acabo de encontrar que la lógica del 'enlace' está muy oculta. Además de eso, no veo la adición de métodos Addxxxx a mis entidades como abarrotar la interfaz. Además, lo encuentro mejor ya que es más explícito. –

+0

Esto no me parece muy legible. – UpTheCreek

1

estoy usando IEnumerable público Añadir | Eliminar métodos de enfoque.

Sin embargo, no me gusta mucho, porque no es intuitivo, y polutes class definition.

Me pregunto por qué las personas no usan CustomCollection donde anulan las funciones Agregar, Eliminar, Reemplazar ?? (como se hace en todas partes en el código MS) ???

0

Respaldo la solución aceptada para esta pregunta, sin embargo, la solución presentada no es completa, ya que el uso de campos privados requiere una configuración adicional en los mapeadores. Para el beneficio de los demás, la siguiente es la solución completa:

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

Tenga en cuenta que la colección expuesta al público deberá ser virtual para NHibernate usarlo.También me gusta hacer que sea un campo de solo lectura que se inicializa cuando se crea la clase para garantizar que exista en todos los escenarios. Aquí está el asignador asociado:

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

El acceso NHibernate le dice a utilizar el campo privado para hacer el mapa valores en el modelo. También hay otras opciones en la propiedad de acceso que permiten el uso de varias configuraciones de nombres.

0

Si tiene una clave externa en su base de datos, y está utilizando Identity (SQL Server) para generar sus claves principales, va a NECESIDAD el vínculo de retroceso del hijo al padre. De lo contrario, el inserto se quejará en el niño porque nhibernate necesita hacer una ida y vuelta para la identificación del padre, pero aún no lo ha hecho.

Lo que terminamos haciendo para deshacernos de los backlinks: use el generador NHibernate HiLo. De esta forma, NHibernate siempre tiene los Id. Que necesita para insertar sus relaciones padre/hijo.

< 3!

Cuestiones relacionadas