2009-06-11 10 views
5

Je rencontre des problèmes lors de l'enregistrement d'une entité dans une base de données SQL Server 2005. J'utilise NHibernate 2.0.0.3002 pour ma couche de persistance. Le mappage est typique, avec un ID entier, comme suitNHibernate "identificateur nul" exception après l'insertion d'une entité

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

J'ai omis le reste pour la brièveté. L'application utilise une classe référentiel avec une méthode de sauvegarde générique comme suit

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(); 
    } 
} 

Lorsque vous appelez saveOrUpdate sur la session, une exception est levée avec un message de « identificateur null ». Quand je vérifie la base de données, la ligne a été insérée avec toutes les valeurs correctes, donc je pense que le problème est quand NHibernate essaie de définir la propriété Id de l'entité avec la valeur retournée par @@ IDENTITY. Je peux voir à travers SQL Profiler que @@ IDENTITY est appelé, donc je ne comprends pas pourquoi l'exception est levée.

Est-ce que quelqu'un d'autre a eu ce problème?

+0

Pouvez-vous afficher le code qui effectue la sauvegarde/mise à jour et lorsque vous essayez d'utiliser cet ID? –

+0

J'ai ajouté le code comme demandé. – gilles27

+0

Quelle version de NHibernate utilisez-vous btw? –

Répondre

8

deux Enregistrer et Supprimer doit se produire dans une transaction et la transaction doit être engagée à la fin.

comme ceci:

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(); 
    } 
} 

s'il vous plaît noter: vous voulez conclure que, dans l'utilisation et correctement rollback ... aussi, le placement de l'endroit où vous ouvrez et engageant la transaction en partant d' votre scénario. Vous devriez également fermer la transaction lorsque vous avez terminé ...

En outre, pouvez-vous préciser où l'exception se produit? Il semble que vous sauviez un parent, puis l'enfant lance parce que l'ID du parent est nul. Ou, est-ce que c'est en train de sauver le parent?

+0

J'ai tout enveloppé dans une transaction comme conseillé, avec rollback appelé si une exception est levée. Cela a résolu mon problème merci. L'enregistrement a des relations avec deux autres enregistrements, mais ils existent tous les deux (et ont donc des ID) bien avant que cet enregistrement soit créé, donc je ne crois pas que cela puisse être lié au problème. Je suis surpris que tout emballer dans une transaction l'ait corrigé, mais je devrais peut-être faire quelques recherches sur les meilleures façons d'utiliser les sessions et les transactions de NHibernate. Tous les pointeurs seraient les bienvenus. – gilles27

+0

Il n'y a vraiment pas de meilleure façon, à mon humble avis. Tout dépend de votre conception spécifique. En général, j'ai des racines agrégées qui spécifient des cascades aux enfants. J'enveloppe ensuite la sauvegarde au niveau de la racine agrégée dans une transaction. Par exemple. Les réponses ont des commentaires, donc je ferais une réponse.AddComment (commentaire); answerRepository.Save (réponse). Ma nouvelle méthode préférée pour l'enregistrement de la sauvegarde dans une transaction provient de la source (ancienne) de FNH .... affichera dans un peu. – Ben

+0

' public void Enregistrer (entité T) { WithinTransaction (() => Session.SaveOrUpdate (entity)); } private void WithinTransaction (Action) { ITransaction transaction = Session.BeginTransaction(); essayez { action(); transaction.Commit(); } catch (Exception) { transaction.Rollback(); lancer; } enfin { transaction.Dispose(); } } ' – Ben

-2

Je pense à la place de la classe génératrice = "identitiy" />

générateur try class = "guid.comp"

+0

Je pense que vous voulez dire guid.comb, pas guid.comp. En tout cas cela ne fonctionnera pas car je travaille avec une clé entière, pas une clé GUID. – gilles27

+1

Bien que cela n'ait pas fonctionné dans ce cas particulier, cela a fonctionné pour moi. Pas une mauvaise réponse – Batman

0

L'identité est assez découragé par les développeurs NHibernate .. le principal problème que nous avons rencontrés est que jusqu'à ce que vous Flush vous n'obtenez pas un Id. Dans votre cas, HiLo serait un bon remplacement.

Cela dit, je pense que vous voulez vraiment faire ...

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

J'ai essayé d'utiliser le XML montré ici mais le problème persiste. Je pense que XML est équivalent au mien ci-dessus, juste un peu plus laconique. – gilles27

1

Il est peut-être pour configurer helpfull log4net afin que vous puissiez enregistrer et voir les actions que NHibernate fait ...

J'ai eu un problème avec NHibernate en utilisant l'accès aussi bien, et a pu le résoudre par la mise en place de la journalisation afin que je puisse identifier exactement la cause du problème.

Le message d'erreur que j'ai reçu, était différente de la vôtre, mais this est l'article où je décris comment je résolu mon problème. Peut-être que cela pourrait être utile pour vous. :)

+0

J'ai utilisé Profiler pour inspecter le SQL en cours d'exécution par NHibernate et tout semble correct. Il appelle même @@ IDENTITY pour récupérer le nouvel ID de l'enregistrement, mais après cela l'exception est levée. Pour être clair, j'utilise SQL Server, pas Access. – gilles27

+0

Je pensais que vous aviez dit dans votre TS que vous utilisiez Access? Quoi qu'il en soit, gentil gars de déclasser toutes les personnes qui ont essayé de vous aider, mais n'a pas fourni la bonne solution ... –

+0

Je m'excuse si je vous ai offensé en déclassant votre réponse. Cependant, je crois dans l'esprit de ce site que les réponses incorrectes ou trompeuses devraient être rejetées afin que le meilleur contenu soit au sommet. Voter pour votre réponse n'est en aucun cas critique à votre égard ou à celui du blog auquel vous vous êtes connecté. Merci d'essayer d'aider. – gilles27

0

J'ai également eu cette erreur et la solution était d'envelopper dans une transaction comme Ben mentionné. Cependant, je n'ai pas réussi à faire fonctionner Ben et gilles27 - probablement parce que je suis nouveau dans les génériques et NHibernate. J'ai créé une implémentation légèrement différente qui fonctionne (en utilisant 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(); 
     } 
    } 
Questions connexes