使用“而不是”触发器在表上进行 DataAdapter.Update/Refresh 后未设置 DataTable 标识列 (SqlServer 2005)
在我们的单元测试中,我们使用普通的 ADO.NET(DataTable、DataAdapter)来准备数据库。检查结果,而测试的组件本身在 NHibernate 2.1 下运行。 .NET版本是3.5,SqlServer版本是2005。
数据库表以标识列作为主键。有些表应用而不是插入/更新触发器(这是由于向后兼容性,我无法更改)。触发器通常如下工作:
create trigger dbo.emp_insert
on dbo.emp
instead of insert
as
begin
set nocount on
insert into emp ...
select @@identity
end
ADO.NET DataAdapter 发出的插入语句(由瘦 ADO.NET 包装器即时生成)尝试将标识值检索回 DataRow:
exec sp_executesql N'
insert into emp (...) values (...);
select id, ... from emp where id = @@identity
'
但 DataRow 的 id-Column 是仍然是 0。当我暂时删除触发器时,它工作正常 - id-Column 然后保存数据库设置的标识值。
另一方面,NHibernate 使用这种插入语句:
exec sp_executesql N'
insert into emp (...) values (...);
select scope_identity()
'
这有效,NHibernate POCO 在刷新后立即正确设置了 id 属性。这对我来说似乎有点违反直觉,因为我期望触发器在不同的范围内运行,因此@@identity应该比scope_identity()更合适。
所以我想没问题,我也会在ADO.NET下应用scope_identity()而不是@@identity。但这没有效果,DataRow值仍然没有相应更新。
现在是最好的部分:当我将这两个语句从 SqlServer 探查器复制并粘贴到 Management Studio 查询(包括“exec sp_executesql”)中,并在那里运行它们时,结果似乎是相反的! ADO.NET 版本可以工作,而 NHibernate 版本则不能(selectscope_identity() 返回 null)。我多次尝试验证,但均无果。好吧,实际上这就是我所期望的 - @@identity 没问题,而scope_identity() 失败。
当然,在 Management Studio 中调用它只是显示来自数据库的结果集,无论 NHibernate 和 ADO.NET 内部发生什么都是另一个话题。此外,T-SQL SET 定义的几个会话属性在两种场景(Management Studio 查询与运行时应用程序)中是不同的,
这对我来说是一个真正的难题。我很高兴对此有任何见解。谢谢你!
Within our unit tests we use plain ADO.NET (DataTable, DataAdapter) for preparing the database resp. checking the results, while the tested components themselves run under NHibernate 2.1. .NET version is 3.5, SqlServer version is 2005.
The database tables have identity columns as primary keys. Some tables apply instead-of-insert/update triggers (this is due to backward compatibility, nothing I can change). The triggers generally work like this:
create trigger dbo.emp_insert
on dbo.emp
instead of insert
as
begin
set nocount on
insert into emp ...
select @@identity
end
The insert statement issued by the ADO.NET DataAdapter (generated on-the-fly by a thin ADO.NET wrapper) tries to retrieve the identity value back into the DataRow:
exec sp_executesql N'
insert into emp (...) values (...);
select id, ... from emp where id = @@identity
'
But the DataRow's id-Column is still 0. When I remove the trigger temporarily, it works fine - the id-Column then holds the identity value set by the database.
NHibernate on the other hand uses this kind of insert statement:
exec sp_executesql N'
insert into emp (...) values (...);
select scope_identity()
'
This works, the NHibernate POCO has its id property correctly set right after flushing. Which seems a little bit counter-intuitive to me, as I expected the trigger to run in a different scope, hence @@identity should be a better fit than scope_identity().
So I thought no problem, I will apply scope_identity() instead of @@identity under ADO.NET as well. But this has no effect, the DataRow value is still not updated accordingly.
And now for the best part: When I copy and paste those two statements from SqlServer profiler into a Management Studio query (that is including "exec sp_executesql"), and run them there, the results seem to be inverse! There the ADO.NET version works, and the NHibernate version doesn't (select scope_identity() returns null). I tried several times to verify, but to no avail. Well, actually that was what I would have expected - @@identity to be OK, and scope_identity() to fail.
Of course invoking it in Management Studio just shows the resultset coming from the database, whatever happens inside NHibernate and ADO.NET is another topic. Also, several session properties defined by T-SQL SET are different in the two scenarios (Management Studio query vs. application at runtime)
This is a real puzzle to me. I would be happy about any insights on that. Thank you!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
找到了。标识值实际上已传输到数据表中,只是不在我期望的列中。 ADO.NET 创建了一个新列“Column1”,而不是使用现有列“id”。
原因是替代触发器末尾的这一行:
不幸的是,NHibernate 似乎需要在替代触发器末尾“select @@identity”(这在 Hibernate 论坛帖子中提到过,我现在再次验证了它) ——确实有必要)。但我可以从这里开始(适应 NHibernate 方言是一种可能性)......
Found it. The identity value actually was transmitted into the DataTable, just not in the column I expected. Instead of using existing column "id", ADO.NET created a new column "Column1".
Reason is this line at the end of the instead-of trigger:
Unfortunately, NHibernate seems to require "select @@identity" at the end of instead-of triggers (this was mentioned in a Hibernate forum posting, and I verified it again now - it is indeed necessary). But I can go along from here (adapting NHibernate dialect is one possibility)...