2009-07-01 14 views
20

Ho due classi:Come salvare un bambino con id assegnato in NHibernate

public class Parent 
{ 
    public virtual long? ID { get; set; } // native 
    public virtual IList<Child> Children { get; set; } 
    public virtual string Name { get; set; } 
} 

public class Child 
{ 
    public virtual long ID { get; set; } // assigned 
    public virtual string Name { get; set; } 
} 

istanziare e genitore risparmio e del bambino:

child = new Child() { ID = 1, Name = "SomeName" }; 
parent = new Parent() { Children = new List() { child } }; 
session.Save(parent); 

che mi dà:

NHibernate. StaleStateException: numero di righe inattese: 0; attesi: 1.

Penso che il problema sia con l'ID assegnato sul bambino. Poiché ha un ID, NHibernate pensa di aver precedentemente salvato prima che non sia il caso.

Il generato (rifilato & rinominato) SQL è:

NHibernate: select child0_.ID as child1_1_, child0_.NAME as NAME1_, child0_.PARENT_ID as COMMAND7_1_, from CHILD child0_ 
NHibernate: select parent0_.PARENT_ID as parent1_10_ 
NHibernate: select parent0_.PARENT_ID as parent1_10_, parent0_.NAME as parent2_10_ from PARENT parent0_ 
NHibernate: UPDATE CHILD SET PARENT_ID = @p0 WHERE CHILD_ID = @p1;@p0 = 2, @p1 = 1 

file di mapping:

<class name="MyNamespace.Child" table="CHILD"> 
    <id name="ID" column="CHILD_ID" type="System.Int64"> 
    <generator class="assigned"></generator> 
    </id> 
    <property name="Name" column="NAME"></property> 
</class> 

<class name="MyNamespace.Parent" table="PARENT"> 
    <id name="ID" column="PARENT_ID" type="System.Int64"> 
    <generator class="native"></generator> 
    </id> 
    <property name="Name" column="NAME"></property> 
    <bag name="Children"> 
    <key column="PARENT_ID"></key> 
    <one-to-many class="MyNamespace.Child"></one-to-many> 
    </bag> 
</class> 

Durante la ricerca google, ho trovato su tag versione che può essere una soluzione, ma non ho un campo persistente da utilizzare come versione. In questo caso, come posso salvare (inserire) un bambino con id assegnato e suo genitore?

+0

Puoi pubblicare i tuoi rapporti di parentela? – mxmissile

+0

Ok, li ho inclusi ora. –

+0

Non sono sicuro che questo risolva il problema o non sia correlato all'id "assegnato", non ho mai lavorato con loro. Ma prova a impostare inverse = "true" sulla proprietà bag. – mxmissile

risposta

28

Quando si passa da un genitore a un figlio, NHibernate utilizza il metodo SaveOrUpdate. Hai ragione sul fatto che NHibernate abbia bisogno di un modo per determinare se debba eseguire un inserimento o un aggiornamento. Analizzerà tre diversi campi per un valore non salvato per determinare se l'entità è nuova.

  1. Id
  2. Versione
  3. timestamp

con un ID assegnato, avrete bisogno di una versione o campo timestamp per indicare che l'entità è nuovo.

Un'alternativa sarebbe chiamare esplicitamente Save() sui figli.

+3

+1 g! Questo mi ha aiutato a trovare una soluzione a un problema con Fluent NHibernate. Le entità di My Child stavano chiamando aggiornamento invece di inserimento. Per risolvere il problema, dovevo aggiungere all'ID mappato, ad es. 'Id (x => x.Id) .UnsavedValue (* defaultValue *);' Dove '* defaultValue *' corrisponde al valore nel mio oggetto, ad es. int: -1, Guid: Guid.Empty –

+0

+1 per la spiegazione più compatta di come nhibernate determina se un'entità è nuova o distaccata sul web. –

+0

Sto avendo un problema simile ma non genitore-figlio. È solo una singola entità con Id (int) assegnato che devo inserire. Non ho colonne Version e Timestamp ma immagino di non averne bisogno per il mio caso. Quando provo a inserire l'entità (semplicemente chiamando il metodo Save()), ottengo la stessa StaleStateException. NHProf mostra che viene inviata un'istruzione UPDATE (ovviamente errata). Qualche idea su cosa potrei fare male e perché ancora NHibernate cerca di aggiornare? – Koen

15

Non sono sicuro al 100% se si tratta dello stesso problema, ma il mio database è assegnato al 100% id (ugh) e ho dovuto creare un Interceptor che tenesse traccia di se un bambino è o non è t ha continuato a far funzionare le cascate.

Il codice è copia/incolla (motivo per cui ha nomi stupidi ... all'inizio non l'ho capito al 100%!) E inizialmente ne ho ottenuto il 90% dalla documentazione online (che non posso trovare tramite Google in questo momento ... mi dispiace):

classe base si mette sul oggetto che ha un ID assegnato vuoi cascata:

public class Persistent 
{ 
    private bool _saved = false; 

    public virtual void OnSave() 
    { 
     _saved = true; 
    } 

    public virtual void OnLoad() 
    { 
     _saved = true; 
    } 

    public virtual bool IsSaved 
    { 
     get { return _saved; } 
    } 
} 

L'intercettore si aggiunge alla sessione:

public class TrackingNumberInterceptor : EmptyInterceptor 
{ 
    public override bool? IsTransient(object entity) 
    { 
     if (entity is Persistent) 
     { 
      return !((Persistent)entity).IsSaved; 
     } 
     else 
     { 
      return null; 
     } 
    } 

    public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types) 
    { 
     if (entity is Persistent) ((Persistent)entity).OnLoad(); 
     return false; 
    } 

    public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types) 
    { 
     if (entity is Persistent) ((Persistent)entity).OnSave(); 
     return false; 
    } 
} 

Fondamentalmente l'ide a è che, poiché NHibernate non sa se un'entità id assegnata è persistente o meno, si tiene traccia di essa.

Per impostazione predefinita l'oggetto inizia con persistente (_saved) su false.Quando l'entità viene caricata o salvata da NHibernate, il trigger imposta su true l'oggetto persistente (_saved).

Quindi per un articolo nuovo che non è persistente, inizia da false e rimane false perché NHibernate non l'ha mai salvato o caricato. Quando NHibernate controlla se il bambino è transitorio, il trigger risponde che è transitorio e si verifica un salvataggio che contrassegna il bambino come persistente. Inoltre ora qualsiasi uso futuro richiederà un carico che lo contrassegna nuovamente come persistente.

+0

Questo particolare metodo ha risolto molti problemi per noi. FYI - la documentazione in cui viene trovato questo codice proviene direttamente dai documenti NHibernate – Doug

+0

che ha risolto anche il mio problema. Molto semplice e facile da applicare. – abx78

-1

Chiamare session.SaveOrUpdate (childObject) dovrebbe risolvere il problema.

Problemi correlati