它应该是显而易见的,但不幸的是它不是。 T-SQL 触发器可以使用@@PROCID,然后在sys.objects 中查找父对象,但对于SQLCLR 则不然。这是一个建议修复此问题的 Microsoft Connection 项:
A SQLCLR trigger should be given the parent object in the SQLTriggerContext
这里有一个关于重复问题的解决方法:
Retrieve the sqlobject that fired the trigger in clr
该变通办法(即使用CONTEXT_INFO)的问题在于,如果以下任一条件为真,它就不起作用:
CONTEXT_INFO 已被用于其他目的。
触发器可以更新一个表,该表上还有一个 SQLCLR 触发器,需要知道其父对象的名称。
但是,使用sp_settriggerorder 过程将顺序设置为First 是正确的。
我确实对此有一个可能可以工作的修复程序,即使在嵌套触发器场景中,但我只是没有时间测试它(请参阅 UPDATE 部分以下)。不过,这个概念是在INSERT, UPDATE, DELETE 触发器中按照以下方式做一些事情(以及将触发器顺序设置为First):
CREATE TRIGGER [SchemaName].[tr_SetTriggerInfo]
ON [SchemaName].[TableName]
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
CREATE TABLE #TriggerInfo (TriggerObjectID INT, TriggerName sysname,
ParentObjectID INT, ParentName sysname, TriggerNestLevel INT);
INSERT INTO #TriggerInfo (TriggerObjectID, TriggerName,
ParentObjectID, ParentName, TriggerNestLevel)
SELECT @@PROCID,
trggr.[name] AS [TriggerName],
trggr.[parent_object_id] AS [ParentObjectID],
OBJECT_NAME(trggr.[parent_object_id]) AS [ParentName],
TRIGGER_NESTLEVEL(@@PROCID, 'AFTER', 'DML') AS [TriggerNestLevel]
FROM sys.objects trggr
WHERE trggr.[object_id] = @@PROCID;
END;
GO
EXEC sp_SetTriggerOrder @TriggerName = N'[SchemaName].[tr_SetTriggerInfo]',
@Order = N'First',
@StmtType = N'DELETE';
EXEC sp_SetTriggerOrder @TriggerName = N'[SchemaName].[tr_SetTriggerInfo]',
@Order = N'First',
@StmtType = N'INSERT';
EXEC sp_SetTriggerOrder @TriggerName = N'[SchemaName].[tr_SetTriggerInfo]',
@Order = N'First',
@StmtType = N'UPDATE';
GO
这应该工作,原因如下:
使用 "Context Connection = true;" 的 ConnectString 的 SQLCLR 触发器可以从本地临时表中读取。
-
本地临时表在嵌套场景中表现如下:
- 如果父上下文中已经存在临时表,则 DML 语句将看到它并可以与之交互,并且这些更改将能够同时用于附加嵌套级别和父级别(非常方便的功能)
- 如果父上下文中已经存在一个临时表,并且为相同的临时表名称执行
CREATE TABLE 语句,而不是出错,它将创建该表的 新 副本,并且父上下文中同名的临时表现在将无法访问(即隐藏)。这意味着,如果您处于第 3 级,那么当该级别结束时,在第 2 级运行的触发器的代码仍将具有第 2 级的值,而不是第 3 级的值。
这对于CONTEXT_INFO 是不可能的,这就是为什么CONTEXT_INFO 不适用于嵌套场景:级别 3 将覆盖级别 2 的值,因此当控制返回到级别 2 时,它现在在CONTEXT_INFO。鉴于我们只能将单个触发器设置为“第一个”而不能控制哪个触发器是“第二个”(除非您的触发器永远不会超过 3 个,在这种情况下您也可以使用“最后一个”位置),那么您不能保证 SQLCLR 触发器将在“第一个”触发器之后立即触发,并且如果另一个触发器触发修改了具有此触发器设置的表,那么您在 CONTEXT_INFO 中的数据已损坏。
更新
实际上,上述临时表解决方案可能不起作用,因为一旦 T-SQL 触发器结束,即 SQLCLR 触发器触发之前,本地临时表可能会消失。下周我还需要测试这个。如果这是实际行为并且本地温度不起作用,那么我有另一个想法应该起作用。