2010-02-05 10 views
8

Tengo dificultades para encontrar la mejor forma de modelar relaciones 1: 0,1 ("puede tener uno" o "tiene como máximo uno"). Creo que esto se llama cardinalidad Z. Por ejemplo, supongamos que tengo dos clases Widget y WidgetTest. No todos los Widgets se prueban y la prueba es destructiva, por lo que puede haber como máximo un WidgetTest por Widget. También suponga que no es apropiado agregar los campos WidgetTest a Widget.Modelado de relaciones uno a cero o uno (cardinalidad Z)

Me gustaría que mi interfaz pública sea:

Widget 
    WidgetTest { get; set; } 

WidgetTest 
    Widget { get; } 

Modelo 1: Widget tiene una propiedad WidgetTest y en la base de datos la tabla widget tiene una clave externa restringida únicamente a WidgetTest. Mi DBA argumenta que esto permitiría que exista un registro WidgetTest sin un Widget.

WidgetTable 
    WidgetTestId (FK, UQ) 

Modelo 2: Widget tiene una colección privada de WidgetTest y hace cumplir la relación 0,1 por adición o eliminación de un único objeto de la colección controlado por un público propiedad WidgetTest. La base de datos modela esto como 1: m con WidgetTest que tiene una clave foránea únicamente restringida para Widget. Yo argumento que esto significa adoptar el modelo para que se ajuste al esquema de la base de datos (es decir, más trabajo para mí).

WidgetTestTable 
    WidgetId (FK, UQ) 

¿Qué modelo es mejor? ¿Qué es más fácil de lograr con NHibernate? ¿O hay una tercera forma?

Editar ... Esto es lo que terminó con:

public class Widget 
{ 
    // This is mapped in NH using a access strategy 
    private IList<WidgetTest> _widgetTests = new List<WidgetTest>(1); 

    public WidgetTest 
    { 
     get { return _widgetTests.FirstOrDefault(); } 
     set 
     { 
      _widgetTests.Clear(); 
      if (value != null) 
      { 
       _widgetTests.Add(value); 
      } 
     } 
    } 
} 
+0

cuál es incorrecto con un 1 :? impuesto por PK-PK. mapeo que bastante recto ... –

Respuesta

4

Mi enfoque ha sido modelar una relación uno a muchos en las asignaciones, pero restringir los "muchos" a un solo elemento. Esto permite el one-to-one opcional, y también garantiza que su instancia de WidgetTest se mantenga cuando guarde el Widget. Por ejemplo:

public class Widget 
{ 
    /// <summary> 
    /// This property is ignored by the NHibernate mappings. 
    /// </summary> 
    public virtual WidgetTest WidgetTest { get; set; } 

    /// <summary> 
    /// For easier persistence with NHibernate, this property repackages the 
    /// WidgetTest property as a list containing a single item. If an 
    /// attempt is made to set this property to a list containing more than 
    /// one item, an exception will be thrown. But why bother? Just use the 
    /// WidgetTest property. 
    /// </summary> 
    public virtual IList<WidgetTest> WidgetTests 
    { 
     get 
     { 
      IList<WidgetTest> widgetTests = new List<WidgetTest>(); 
      if (this.WidgetTest != null) 
      { 
       widgetTests.Add(this.WidgetTest); 
      } 
      return widgetTests; 
     } 
     set 
     { 
      if (value != null && value.Count > 1) 
      { 
       throw new Exception("The WidgetTests collection may not contain more than one item."); 
      } 
      else if (value != null && value.Count == 1) 
      { 
       this.WidgetTest = value[0]; 
      } 
      else 
      { 
       this.WidgetTest = null; 
      } 
     } 
    } 
} 
+0

Este es más o menos mi "Modelo 2" y he hecho algo similar en el pasado. Sin embargo, mapeo la colección como un miembro privado y no la expongo. Estaba pensando un poco más y este modelo funciona para una relación 1: n. Me estoy colgando cuando n = 1. –

+0

Ver la edición de mi pregunta para la solución en la que me decidí. Esta respuesta es la más cercana, así que la acepté. –

+0

Me gusta su variación ... muy conciso. ¿Estaría dispuesto a compartir el mapeo de NH que utilizó? –

1

Cuando dice "asuma que no es apropiado para agregar los campos WidgetTest a Widget", qué se refiere en sus objetos de dominio o en la base de datos . Si está contento de que los campos estén en la misma tabla en la base de datos, ¿qué le parece asociar WidgetTest como un componente de Widget? Tener la mirada archivo de asignación NHibernate como:

<class name="Widget" table="Widget"> 
    ... 
    <property name="WidgetProperty"/> 
    ... 
    <component name="WidgetTest" class="WidgetTest"> 
     <property name="WidgetTestProperty"/> 
    </component> 
</class> 

Dando la estructura de tabla:

WidgetTable 
    WidgetProperty 
    WidgetTestProperty 

que todavía le permiten tener la interfaz pública que ha especificado, sin embargo, WidgetTest se convertiría en un objeto de valor, que usted puede o no querer.

+0

quiero decir en la tabla. Además de agregar más de un centenar de campos adicionales, tendrían que establecer por defecto nulo, lo que no es deseado. Estoy más interesado en la mejor manera de modelar relaciones 1: 0,1 en general que para una aplicación específica. –

0

tengo otros 2 Ideas here

  • tabla de unión y el mapa como Componente
  • ignoran Id de la clase depende
0

El answer given by nw. puede dar lugar a la excepción " A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance ".

Encontrará que este es el caso si está usando inverse="true" y cascade="all-delete-orphan" en su archivo de mapeo.

Esto se debe a que la respuesta de nw. Crea una nueva lista cada vez que se llama al descriptor de acceso get y no hace nada con la lista pasada a través del descriptor de acceso set. Como tal, NHibernate no tiene la referencia IList<WidgetTest> que originalmente pasó al crear el objeto y no puede continuar con la cascada.

Para solucionar esto, tenemos que hacer algo con esa referencia IList<WidgetTest> y tener cuidado de no desviarla.

public class Widget 
{ 
    public Widget() 
    { 
     _widgetTests = new List<WidgetTest>(); 
    } 

    /// <summary> 
    /// This property is ignored by the NHibernate mappings. 
    /// </summary> 
    public WidgetTest WidgetTest { get; set; } 

    /// <summary> 
    /// For easier persistence with NHibernate, this property repackages the 
    /// WidgetTest property as a list containing a single item. If an 
    /// attempt is made to set this property to a list containing more than 
    /// one item, an exception will be thrown. But why bother? Just use the 
    /// WidgetTest property. 
    /// </summary> 
    private IList<WidgetTest> _widgetTests; 
    protected virtual IList<WidgetTest> WidgetTests 
    { 
     get 
     { 
      if (_widgetTests.Count == 0 && WidgetTest != null) 
      { 
       _widgetTests.Add(WidgetTest); 
      } 
      else if (_widgetTests.Count > 0 && WidgetTest == null) 
      { 
       _widgetTests.Clear(); 
      } 
      else if (_widgetTests.Count > 0 && WidgetTest != _widgetTests[0]) 
      { 
       _widgetTests.Clear(); 
       _widgetTests.Add(WidgetTest); 
      } 
      return _widgetTests; 
     } 
     set 
     { 
      if (value != null && value.Count > 1) 
      { 
       throw new Exception("The WidgetTest collection may not contain more than one item."); 
      } 
      if (value != null && value.Count == 1) 
      { 
       WidgetTest = value[0]; 
      } 
      else 
      { 
       WidgetTest = null; 
      } 

      //Store the reference 
      _widgetTests = value; 
     } 
    } 
} 

Mapping:

<class name="Widget" table="widgets"> 
    ... 
    <id name="Id" type="Guid" column="widgetId"> 
     ... 
    </id> 
    ...     
    <bag name="WidgetTests" inverse="true" cascade="all-delete-orphan" access="property"> 
     ... 
     <key column="widgetId" /> 
     <one-to-many class="WidgetTest" /> 
    </bag> 

    </class> 

La inspiración para la mejora:

http://www.onkarjoshi.com/blog/188/hibernateexception-a-collection-with-cascade-all-delete-orphan-was-no-longer-referenced-by-the-owning-entity-instance/comment-page-1/

Cuestiones relacionadas