2010-02-05 14 views
8

Sto cercando di trovare il modo migliore per modellare le relazioni 1: 0,1 ("può avere uno" o "ha al massimo uno"). Credo che questo si chiami cardinalità Z.Modellazione da uno a zero o una relazione (cardinalità Z)

Ad esempio, supponiamo di avere due classi Widget e WidgetTest. Non tutti i widget sono testati e il test è distruttivo, quindi può esserci al massimo un WidgetTest per Widget. Supponiamo inoltre che sia inappropriato aggiungere i campi WidgetTest al Widget.

vorrei che la mia interfaccia pubblica di essere:

Widget 
    WidgetTest { get; set; } 

WidgetTest 
    Widget { get; } 

Modello 1: Widget ha una proprietà WidgetTest e nella banca dati della tabella Widget ha una chiave esterna vincolata unicamente per WidgetTest. Il mio DBA sostiene che ciò consentirebbe a un record WidgetTest di esistere senza un Widget.

WidgetTable 
    WidgetTestId (FK, UQ) 

Modello 2: Widget ha una collezione privata di WidgetTest e rafforza il rapporto 0,1 con l'aggiunta o la rimozione di un singolo oggetto della collezione controllato da una proprietà WidgetTest pubblica. Il database la modella come 1: m con WidgetTest che ha una chiave esterna vincolata in modo univoco al Widget. Io sostengo che questo significa adottare il modello per adattarlo allo schema del database (cioè più lavoro per me).

WidgetTestTable 
    WidgetId (FK, UQ) 

Quale modello è migliore? Quale è più facile da ottenere con NHibernate? O c'è una terza via?

Modifica ... Ecco cosa ho finito 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

cosa è sbagliato con un 1 :? applicato da PK-PK. mappatura che abbastanza dritto .. –

risposta

4

Il mio approccio è stato quello di modellare una relazione uno-a-molti nelle mappature, ma di vincolare i "molti" a un singolo elemento. Ciò consente l'uno-a-uno opzionale e garantisce inoltre che l'istanza WidgetTest venga mantenuta quando si salva il widget. Per esempio:

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

Questo è più o meno il mio "Modello 2" e ho fatto qualcosa di simile in passato. Mappo la collezione come membro privato e non la espongo comunque. Ci stavo pensando un po 'di più e questo modello funziona per una relazione 1: n. Mi sto bloccando quando n = 1. –

+0

Vedere la modifica alla mia domanda per la soluzione su cui mi sono stabilito. Questa risposta è la più vicina, quindi l'ho accettata. –

+0

Mi piace la tua variazione ... molto concisa. Saresti disposto a condividere la mappatura NH che hai usato? –

1

Quando si dice "dare per scontato che è opportuno aggiungere i campi WidgetTest a Widget", vuoi dire nei vostri oggetti di dominio o nel database . Se sei felice che i campi siano nella stessa tabella del database, che ne dici di mappare WidgetTest come componente di Widget? Hanno l'aspetto file di mapping NHibernate come:

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

Dare la struttura della tabella:

WidgetTable 
    WidgetProperty 
    WidgetTestProperty 

Quale sarebbe ancora ti permettono di avere l'interfaccia pubblica che hai specificato, tuttavia, WidgetTest sarebbe diventato un oggetto di valore, che potresti o non vorresti.

+0

Voglio dire nel tavolo. Oltre all'aggiunta di oltre un centinaio di campi aggiuntivi, dovrebbero predefinire il valore null che è indesiderato. Sono più interessato al modo migliore per modellare le relazioni 1: 0,1 in generale che per un'applicazione specifica. –

0

ho 2 altre idee here

  • Registrato tavolo e mappa come componente
  • ignorare Id della classe dipendente
0

Il answer given by nw. può causare l'eccezione " A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance ".

Questo è il caso se si utilizza inverse="true" e cascade="all-delete-orphan" nel file di mapping.

Questo perché la risposta di nw. Crea un nuovo elenco ogni volta che viene richiamata l'accessor get e non fa nulla con l'elenco passato attraverso l'accessorio set. Come tale, NHibernate non ha il riferimento IList<WidgetTest> originariamente passato durante la creazione dell'oggetto e non può procedere con la cascata.

Quindi, per risolvere questo problema, dobbiamo fare qualcosa con quel riferimento IList<WidgetTest> e fare attenzione a non de-referenziare.

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> 

L'ispirazione per la valorizzazione:

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/

Problemi correlati