Domanda

Sto usando NHibernate / Fluente NHibernate in un'app ASP.NET MVC con un database MySQL. Sto lavorando a un'operazione che legge un bel po 'di dati (relativamente a quanto viene inserito), li elabora e finisce per inserire (attualmente) circa 50 record. Ho una ISession per richiesta che viene creata / distrutta nei gestori di eventi di richiesta di inizio / fine (esattamente come http://ayende.com/Blog/archive/2009/08/06/challenge-find-the-bug-fixes.aspx ), e sto leggendo i dati e aggiungendo nuovi oggetti (come nella sezione 16.3 a https://www.hibernate.org/hib_docs/nhibernate/html/example-parentchild.html ) e infine chiamando Flush () sulla sessione per eseguire effettivamente tutti gli inserti.

Il recupero dei dati e il caricamento lento funzionano correttamente, e quando chiamo Flush vengono inseriti esattamente 2 nuovi record (sto controllando manualmente la tabella per scoprirlo), e quindi ottengo il seguente errore:

  

NonUniqueObjectException: un oggetto diverso con lo stesso   il valore identificativo era già   associato alla sessione: 0, di   entità: ...

Sono nuovo di NHibernate e durante la ricerca di una soluzione ho provato a impostare esplicitamente il generatore della proprietà Id su Native e Identity (è un database MySQL e la colonna Id è un int con auto_increment attivo) e l'impostazione esplicita del non salvato valore per la proprietà Id su 0. Ricevo comunque l'errore.

Ho anche provato a chiamare Flush in momenti diversi (effettivamente una volta per INSERT) e quindi ottengo lo stesso errore, ma per un valore di identità diverso da 0 e in punti apparentemente casuali nel processo (a volte non lo capisco in tutto in questo scenario, ma a volte lo faccio in diversi punti).

Non sono sicuro di dove andare da qui. Qualsiasi aiuto o approfondimento sarebbe molto apprezzato.

MODIFICA: vedere la risposta di seguito.

È stato utile?

Soluzione

MODIFICA: inizialmente avevo pubblicato una diversa "risposta" che in realtà non ha risolto il problema, ma desidero documentare le mie scoperte qui per chiunque possa trovarlo.

Dopo diversi giorni in cui ho cercato di capire il problema e di risolverlo ed essere estremamente frustrato perché il problema sembrava scomparire per un po 'e poi tornare in modo intermittente (facendomi pensare più volte che una modifica che ho apportato ha risolto il problema , quando in realtà non lo ha fatto), credo di aver rintracciato il vero problema.

Alcune volte dopo aver impostato il livello log4net per NHibernate su DEBUG, il problema è scomparso, ma alla fine sono riuscito a ottenere l'errore con quel livello di registro. Nel registro c'erano queste righe:

Building an IDbCommand object for the SqlString: SELECT LAST_INSERT_ID()
...
NHibernate.Type.Int32Type: 15:10:36 [8] DEBUG NHibernate.Type.Int32Type: returning '0' as column: LAST_INSERT_ID()
NHibernate.Id.IdentifierGeneratorFactory: 15:10:36 [8] DEBUG NHibernate.Id.IdentifierGeneratorFactory: 
Natively generated identity: 0

E guardando solo alcune righe ho visto:

NHibernate.AdoNet.ConnectionManager: 15:10:36 [8] DEBUG NHibernate.AdoNet.ConnectionManager: aggressively releasing database connection
NHibernate.Connection.ConnectionProvider: 15:10:36 [8] DEBUG NHibernate.Connection.ConnectionProvider: Closing connection

Sembra che durante lo svuotamento della sessione e l'esecuzione degli INSERT, NHibernate stesse chiudendo la connessione tra l'istruzione INSERT e il " SELECT LAST_INSERT_ID () " per ottenere l'id che è stato generato da MySQL per l'istruzione INSERT. O meglio, dovrei dire che era a volte chiudere la connessione, motivo per cui credo che il problema fosse intermittente. Non riesco a trovare il link ora, ma credo di aver letto in tutte le mie ricerche che MySQL a volte restituirà il valore corretto da LAST_INSERT_ID () anche se la connessione viene chiusa e riaperta, che è un altro motivo per cui credo fosse intermittente. Il più delle volte, tuttavia, LAST_INSERT_ID () restituirà 0 se la connessione viene chiusa e riaperta dopo INSERT.

Sembra che ci siano 2 modi per risolvere questo problema. La prima è una patch disponibile qui che sembra che lo farà in NHibernate 2.1.1, o che puoi usare per creare la tua build di NHibernate, che forza INSERT e SELECT LAST_INSERT_ID () a correre insieme. In secondo luogo, è possibile impostare connection.release_mode su on_close come descritto in questo post sul blog che impedisce a NHibernate di chiudere la connessione fino alla chiusura esplicita di ISession.

Ho adottato il secondo approccio, che è fatto in FluentNHibernate in questo modo:

Fluently.Configure()
    ...
    .ExposeConfiguration(c => c.Properties.Add("connection.release_mode", "on_close"))
    ...

Ciò ha avuto anche l'effetto collaterale di accelerare drasticamente il mio codice. Ciò che stava impiegando 20-30 secondi per funzionare (quando è successo così prima che io apportassi questa modifica) è ora in esecuzione in 7-10 secondi, quindi sta facendo lo stesso lavoro in ~ 1/3 il tempo.

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