NHibernate: la relazione molti-a-molti non riesce a salvare prima gli oggetti figlio (o: & # 8220; impossibile inserire null & # 8221; oppure: & # 8220; oggetto transitorio & # 8221;)

StackOverflow https://stackoverflow.com/questions/1819851

Domanda

Ho una classe di invio che aggrega una classe FreightDateTime. Allo stesso tempo, anche la classe FreightDateTime viene aggregata dalla classe GoodsItem. Allo stesso modo, FreightDateTime è associato a un numero di altre classi che ho lasciato fuori per ora.

Per evitare un FreightDateTime impostabile in banca dati con chiave esterna di spedizione, una chiave esterna di merci e così via, ho deciso che l'associazione doveva essere molte-a-molte. In questo modo, NHibernate genererebbe invece una tabella di associazione per ogni relazione (ConsigmentFreightDateTimes, GoodsItemFreightDateTimes), il che ha più senso.

Quindi, nel file di mappatura, l'associazione appare ad es. in questo modo:

<bag name="DateTimes" table="FreightDateTimes" lazy="false" cascade="all">
  <key column="ConsignmentId"/>
  <many-to-many class="Logistics.FreightDateTime, Logistics" column="DateTimeId" />
</bag>

Impostazione della cascata su " tutto " rese:

System.Data.SqlClient.SqlException: Cannot insert the value NULL into column 'DateTimeId', table 'LogiGate.dbo.FreightDateTimes'; column does not allow nulls. INSERT fails. 

Impostazione della cascata su " nessuno " rese:

NHibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: Logistics.FreightDateTime

In entrambi i casi, ciò significa che NHibernate sta tentando di salvare l'istanza di consegna, sebbene le istanze secondarie di FreightDateTime non siano state salvate. Nel primo caso, la chiave esterna è ancora 'null', che quindi non può essere inserita nella tabella risultante, e nel secondo caso, NHibernate è consapevole che l'istanza non è stata ancora salvata e quindi genera l'eccezione.

Quindi la domanda è come posso ottenere NHibernate per salvare prima tutte le istanze figlio senza dirlo esplicitamente di farlo. Ho la sensazione che consentire i null sulla colonna DateTimeId farebbe il trucco, ma penso che non sia né desiderabile né possibile.

È stato utile?

Soluzione

Prova anche a mappare l'associazione dall'altra parte ma usa l'inverso = " true " attributo da quel lato. Quindi, crea nel file di mappatura FreightDateTime un sacchetto per mappare l'associazione come molti-a-molti con la spedizione.

Inoltre, ho risposto a una domanda simile qui: Qual è il modo corretto di definire relazioni molti-a-molti in NHibernate per consentire l'eliminazione ma evitando record duplicati

Leggere la domanda e la mia risposta forse ti aiuterà a capire cosa sta succedendo con la tua associazione molti-a-molti e forse ti darà un suggerimento per la soluzione. La raccomandazione finale è una specie di ultima risorsa.

La risposta alla domanda sopra è solo per dare un'idea di ciò che sta attraversando il problema diverso di un'altra persona.

Una soluzione sarebbe quella di mappare esplicitamente la tabella delle associazioni. Se le tabelle sono: Persona, Nota e la tabella di associazione (tabella X) è PersonNote Le tue mappature dovrebbero apparire così:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Person" table="Person" lazy="true">

    <id name="PersonId">
      <generator class="native" />
    </id>
    <property name="FirstName" />
    .....
    <bag name="PersonNotes" generic="true" inverse="true" lazy="true" cascade="none">
        <key column="PersonId"/>
        <one-to-many class="PersonNote"/>
    </bag>

    <bag name="Notes" table="PersonNote" cascade="save-update">
      <key column="PersonId"></key>
      <many-to-many class="Note" column="NoteId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="..."
                   namespace="...">

  <class name="Note" table="Note" lazy="true">

    <id name="NoteId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="Title" />
    ....
    <bag name="PersonNotes" inverse="true" lazy="true" cascade="all-delete-orphan">
        <key column="NoteId"/>
        <one-to-many class="PersonNote"/>
    </bag>

    <bag name="People" table="PersonNote" inverse="true" cascade="save-update" generic="true">
      <key column="NoteId"></key>
      <many-to-many class="Person" column="PersonId"></many-to-many>
    </bag>

  </class>

</hibernate-mapping>

Come sopra, ti consente quanto segue:

  1. Elimina una persona ed elimina solo la voce nella tabella dell'associazione senza eliminare alcuna delle Note
  2. Elimina una nota ed elimina solo la voce nella tabella dell'associazione senza eliminare nessuna delle entità Persona
  3. Salva con Cascades solo dal lato Persona popolando la raccolta Person.Notes e salvando la persona.
  4. Poiché l'inverso = vero è necessario nel Note.People non c'è modo di fare il salvataggio in cascata da questo lato. Popolando la raccolta Note.People e quindi salvando l'oggetto Note si otterrà un inserto nella tabella Note e un inserto nella tabella Persona ma nessun inserto nella tabella delle associazioni. Immagino sia così che funziona NHibernate e non ho ancora trovato un modo per aggirarlo.
  5. È possibile eseguire il collegamento in cascata delle voci di salvataggio nella tabella delle associazioni solo esplicitamente aggiungendo nuovi elementi nella raccolta PersonNotes dell'entità Note.

Tutti i precedenti sono testati con unit test. Sarà necessario creare il file e la classe di mappatura della classe PersonNote affinché funzioni sopra.

Se hai bisogno di un'altra classe per avere note, ad esempio Organizzazione, aggiungi solo un'altra tabella di associazione al tuo schema chiamata OrgnanisationNote e fai lo stesso come sopra per il file di mappatura dell'organizzazione e il file di mappatura delle note.

Devo ripetere ancora una volta che questa dovrebbe essere l'ultima opzione per chiunque voglia avere il controllo completo sulle sue associazioni molte-a-molte.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top