在 DataAdapter.Update/Refresh 后未设置 DataTable 标识列,使用“而不是”触发器(SqlServer 2005)
Posted
技术标签:
【中文标题】在 DataAdapter.Update/Refresh 后未设置 DataTable 标识列,使用“而不是”触发器(SqlServer 2005)【英文标题】:DataTable identity column not set after DataAdapter.Update/Refresh on table with "instead of"-trigger (SqlServer 2005) 【发布时间】:2010-03-31 08:01:41 【问题描述】:在我们的单元测试中,我们使用普通的 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 版本不工作(选择 scope_identity() 返回 null)。我多次尝试验证,但无济于事。好吧,实际上这正是我所期望的——@@identity 没问题,而 scope_identity() 失败了。
当然,在 Management Studio 中调用它只会显示来自数据库的结果集,无论 NHibernate 和 ADO.NET 内部发生什么,都是另一个话题。此外,T-SQL SET 定义的几个会话属性在两种场景(Management Studio 查询与运行时的应用程序)中有所不同
这对我来说是一个真正的难题。我会很高兴对此有任何见解。谢谢!
【问题讨论】:
【参考方案1】:找到了。标识值实际上已传输到 DataTable 中,只是不在我预期的列中。 ADO.NET 没有使用现有列“id”,而是创建了一个新列“Column1”。
原因是instead-of触发器末尾的这一行:
select @@identity
不幸的是,NHibernate 似乎需要在而不是触发器的末尾“选择@@identity”(这在 Hibernate 论坛帖子中提到过,我现在再次验证了它 - 这确实是必要的)。但是我可以从这里开始(适应 NHibernate 方言是一种可能性)...
【讨论】:
以上是关于在 DataAdapter.Update/Refresh 后未设置 DataTable 标识列,使用“而不是”触发器(SqlServer 2005)的主要内容,如果未能解决你的问题,请参考以下文章
在 React 应用程序中在哪里转换数据 - 在 Express 中还是在前端使用 React?