2009-06-11 6 views
5

Ho problemi a salvare un'entità in un database di SQL Server 2005. Sto usando NHibernate 2.0.0.3002 per il mio livello di persistenza. La mappatura è tipica, con un ID intero, come segueErrore di "null identificativo" di NHibernate dopo l'inserimento di un'entità

<id name="Id" unsaved-value="0"> 
    <column name="Id"/> 
    <generator class="identity" /> 
</id> 

Ho omesso il resto per brevità. L'applicazione utilizza una classe repository con un metodo save generico come segue

public void Save(T toSave) 
{ 
    Save(new T[] { toSave }); 
} 

public void Save(IEnumerable<T> toSave) 
{ 
    using (ISession session = SessionFactory.OpenSession()) 
    { 
     foreach (T item in toSave) 
     { 
      session.SaveOrUpdate(item); 
     } 
     session.Flush(); 
    } 
} 

Quando si chiama SaveOrUpdate sulla sessione, viene generata un'eccezione con un messaggio di "identificatore nullo". Quando controllo il database, la riga è stata inserita con tutti i valori corretti, quindi penso che il problema si verifichi quando NHibernate tenta di impostare la proprietà Id dell'entità con il valore restituito da @@ IDENTITY. Posso vedere attraverso SQL Profiler che @@ IDENTITY viene chiamato così non capisco perché l'eccezione è lanciata.

Qualcun altro ha avuto questo problema?

+0

È possibile visualizzare il codice che salva/aggiorna effettivamente e quando si tenta di utilizzare tale ID? –

+0

Ho aggiunto il codice come richiesto. – gilles27

+0

Quale versione di NHibernate stai usando btw? –

risposta

8

Sia Salva ed Elimina deve accadere in una transazione e la transazione deve essere impegnato alla fine.

in questo modo:

public void Save(IEnumerable<T> toSave) 
{ 
    using (ISession session = SessionFactory.OpenSession()) 
    { 
     ITransaction transaction = Session.BeginTransaction(); 

     foreach (T item in toSave) 
     { 
      session.SaveOrUpdate(item); 
     } 

     transaction.Commit();   
     session.Flush(); 
    } 
} 

si prega di notare: si vorrà per avvolgere che in un uso corretto e rollback ... inoltre, il posizionamento di dove si sta aprendo e commettendo la transazione verrà questione sulla base di il tuo scenario. Dovresti anche chiudere la transazione quando hai finito ...

Inoltre, puoi approfondire dove sta succedendo l'eccezione? Sembra che tu stia salvando un genitore e poi il bambino sta lanciando perché l'id del genitore è nullo? O, in realtà sta lanciando il salvataggio del genitore?

+0

Ho completato tutto in una transazione come consigliato, con il rollback chiamato se viene lanciata un'eccezione. Questo ha risolto il mio problema grazie. Il record ha relazioni con altri due record, ma entrambi esistono (e quindi hanno ID) molto prima che venga creato questo record, quindi non credo che possa essere correlato al problema. Sono sorpreso che l'averlo completato in una transazione lo abbia risolto, ma forse ho bisogno di fare qualche ricerca sui modi migliori di usare le sessioni e le transazioni di NHibernate. Qualsiasi suggerimento sarebbe benvenuto. – gilles27

+0

Non c'è davvero il modo migliore, imho. Tutto dipende dal tuo design specifico. In genere, ho radici aggregate che specificano cascate ai bambini. Quindi avvolgo il salvataggio al livello radice aggregato in una transazione. Per esempio. Le risposte hanno commenti quindi vorrei fare una risposta. AddComment (commento); answerRepository.Save (risposta). Il mio nuovo metodo preferito per avvolgere il salvataggio in una transazione proviene dalla fonte (vecchia) di FNH .... pubblicherà un po '. – Ben

+0

' vuoto pubblico Salva (entità T) { WithinTransaction (() => Session.SaveOrUpdate (entity)); } private void WithinTransaction (Azione azione) { ITransazione transazione = Session.BeginTransaction(); prova { azione(); transaction.Commit(); } catch (Exception) { transaction.Rollback(); lancio; } infine { transaction.Dispose(); } } ' – Ben

-2

penso invece di generatore class = "identitiy" />

generatore di prova class = "guid.comp"

+0

Penso che tu intenda guid.comb, non guid.comp. In ogni caso, non funzionerà poiché sto lavorando con una chiave intera, non con una chiave GUID. – gilles27

+1

Anche se questo non ha funzionato in questo caso particolare, ha funzionato per me. Non è una cattiva risposta – Batman

0

identità è abbastanza scoraggiato dagli sviluppatori NHibernate .. il problema principale che abbiamo incontrato è che fino a che non arrossisci non ottieni un ID. Nel tuo caso Int HiLo sarebbe un buon sostituto.

Detto questo credo che in realtà si vuole fare questo ...

<id name="Id" column="Id" unsaved-value="0"> 
    <generator class="identity"/> 
</id> 
+0

Ho provato a usare l'XML mostrato qui ma il problema rimane. Penso che XML sia equivalente al mio sopra, solo un po 'più conciso. – gilles27

1

E 'forse utile per configurare log4net in modo che sia possibile accedere e visualizzare le azioni che NHibernate sta facendo ...

una volta ho avuto un problema con NHibernate utilizzando Access pure, ed è stato in grado di risolverlo da impostando la registrazione in modo da poter individuare esattamente la causa del problema.

Il messaggio di errore che ho ricevuto, era diverso dal tuo, ma this è l'articolo in cui descrivo come ho risolto il mio problema. Forse potrebbe essere utile per voi. :)

+0

Ho usato Profiler per ispezionare l'SQL che viene eseguito da NHibernate e tutto sembra corretto. Chiama anche @@ IDENTITY per recuperare il nuovo ID del record, ma successivamente viene generata l'eccezione. Giusto per essere chiari, sto usando SQL Server, non Access. – gilles27

+0

Pensavo che una volta hai detto nel tuo TS che stai usando Access? Ad ogni modo, bel tizio per declassare tutte le persone che hanno cercato di aiutarti, ma non ha fornito la soluzione corretta ... –

+0

Mi scuso se ti ho offeso declassando la tua risposta. Tuttavia, credo nello spirito di questo sito che le risposte errate o fuorvianti debbano essere revocate affinché il miglior contenuto sia al top. La votazione della tua risposta non è in alcun modo critica per te o per il post del blog a cui ti sei collegato. Grazie per aver tentato di aiutare. – gilles27

0

Avevo anche questo errore e la soluzione era di avvolgerlo in una transazione come menzionato da Ben. Tuttavia, non ero in grado di far funzionare Ben né il codice gilles27, probabilmente perché sono nuovo di generici e di NHibernate. Ho creato un'implementazione leggermente diversa che funziona (usando Fluent NHibernate v1.3):

 public static ISession LocalDbSession = null; 

    public static void Save<T>(T toSave) 
    { 
     using (var transaction = LocalDbSession.BeginTransaction()) 
     { 
      LocalDbSession.Save(toSave); 
      transaction.Commit(); 
      LocalDbSession.Flush(); 
     } 
    } 

    public static void Save<T>(IEnumerable<T> toSave) 
    { 
     using (var transaction = LocalDbSession.BeginTransaction()) 
     { 
      foreach (T item in toSave) 
      { 
       LocalDbSession.Save(item); 
      } 

      transaction.Commit(); 
      LocalDbSession.Flush(); 
     } 
    } 
Problemi correlati