0

Dans nos tests unitaires, nous utilisons ADO.NET (DataTable, DataAdapter) pour la préparation de la base de données resp. vérifier les résultats, tandis que les composants testés eux-mêmes s'exécutent sous NHibernate 2.1. La version .NET est 3.5, la version SqlServer est 2005.Colonne d'identité DataTable non définie après DataAdapter.Update/Refresh sur table avec "au lieu de" -trigger (SqlServer 2005)

Les tables de base de données ont des colonnes d'identité en tant que clés primaires. Certaines tables appliquent des déclencheurs à la place de l'insertion/mise à jour (cela est dû à la rétrocompatibilité, je ne peux rien changer). Les déclencheurs fonctionnent généralement comme ceci:

create trigger dbo.emp_insert 
    on dbo.emp 
    instead of insert 
as 
begin 
    set nocount on 
    insert into emp ... 
    select @@identity 
end 

L'instruction d'insertion émis par le ADO.NET DataAdapter (généré sur la volée par une enveloppe mince ADO.NET) tente de récupérer la valeur d'identité de nouveau dans le DataRow:

exec sp_executesql N' 
insert into emp (...) values (...); 
select id, ... from emp where id = @@identity 
' 

Mais id-colonne du DataRow est toujours 0. Lorsque je supprime temporairement le déclencheur, il fonctionne très bien - l'id-colonne contient la valeur d'identité définie par la base de données.

NHibernate d'autre part utilise ce type d'instruction d'insertion:

exec sp_executesql N' 
insert into emp (...) values (...); 
select scope_identity() 
' 

Cela fonctionne, le NHibernate POCO a sa propriété id correctement défini juste après le rinçage. Ce qui me semble un peu contre-intuitif, car je m'attendais à ce que le déclencheur fonctionne dans une portée différente, donc @@ identity devrait être mieux adapté que scope_identity().

Donc, je pensais pas de problème, je vais appliquer scope_identity() au lieu de @@ identity sous ADO.NET ainsi. Mais cela n'a aucun effet, la valeur DataRow n'est toujours pas mise à jour en conséquence. Et maintenant pour le mieux: lorsque je copie et collez ces deux instructions de profileur SqlServer dans une requête Management Studio (y compris "exec sp_executesql"), et les exécutez là, les résultats semblent être inverses! Là, la version ADO.NET fonctionne, et la version NHibernate ne fonctionne pas (sélectionnez scope_identity() renvoie null). J'ai essayé plusieurs fois de vérifier, mais en vain. Eh bien, en fait c'était ce que j'aurais attendu - @@ identity pour être OK, et scope_identity() pour échouer.

Bien sûr, l'invoquer dans Management Studio montre simplement le jeu de résultats provenant de la base de données, quoi qu'il arrive dans NHibernate et ADO.NET est un autre sujet. En outre, plusieurs propriétés de session définies par T-SQL SET sont différentes dans les deux scénarios (requête Management Studio vs application au moment de l'exécution)

Ceci est un vrai casse-tête pour moi. Je serais heureux de toute idée à ce sujet. Je vous remercie!

Répondre

1

Trouvé. La valeur d'identité a été réellement transmise dans le DataTable, mais pas dans la colonne que je m'attendais. Au lieu d'utiliser la colonne existante "id", ADO.NET a créé une nouvelle colonne "Column1".

La raison est cette ligne à la fin du lieu-de déclencheur:

select @@identity 

Malheureusement, NHibernate semble exiger « sélectionner l'identité @@ » à la fin de la place-des déclencheurs (cela a été mentionné dans un message sur le forum Hibernate, et je l'ai vérifié à nouveau maintenant - c'est en effet nécessaire). Mais je peux partir d'ici (adapter le dialecte de NHibernate est une possibilité) ...