访问 SQLCLR 触发器中的触发/父 table 名称
Accessing the triggered / parent table name inside a SQLCLR trigger
我正在用 C# 创建一个相对简单的 SQL CLR。
此 CLR 的工作是将事件发布到 Azure 事件网格主题。
我希望能够从任意数量的触发器为任意数量的 table 调用这个特定的 CLR(实际上很少)。
鉴于下面的核心代码,如何准确检索 caused/fired 触发器的 table 的名称?我不知道从哪里开始。
我希望能够删除 SqlTrigger
属性并以某种方式访问 var table
属性.
的 table 名称
// I Would like to leave the attribute commented out
//
//[Microsoft.SqlServer.Server.SqlTrigger(Name = "PublishToEventGridTrigger",
// Target = "NamesOfThings", Event = "FOR UPDATE, INSERT, DELETE")]
public static void PublishToEventGridTrigger()
{
// TODO - How should these settings be handled?
// Not sure if config files are accessible... To test..
// ***************************************************************
string topicHost = "https://####.eventgrid.azure.net/api/events";
string topicKey = "####";
// TODO - Get Table name for this
var table = "How Do I set This";
string eventType = $"##.{table}.{SqlContext.TriggerContext.TriggerAction.ToString()}";
// ***************************************************************
try
{
// extract data involved in trigger
// Create the Event object
EventGridEvent evt = new EventGridEvent("QWDBEvent", eventType, new
TriggerData(SqlContext.TriggerContext, SqlContext.Pipe));
// Publish the event
Event.Publish(topicHost, topicKey, evt);
}
catch (Exception ex)
{
//TODO - how do we handle these through SQL CLR?
// Going with a fire-and-forget for now
SqlContext.Pipe.Send($"Failure firing {ex.Message}");
}
SqlContext.Pipe.Send($"Trigger fired");
}
这不是本地支持的,也不是很容易实现。我实际上有一个模型可以做到这一点,但还没有时间发布它。当然不是直截了当的。目前,你可以看看这个问题的两个答案:
我会在清理并发布我的解决方案后更新此答案。
HOWEVER,虽然您说只有少数 table 会应用此功能,但请记住触发器是在DML(甚至 DDL)语句所属的事务的上下文。这有两个后果:
- 触发器花费的时间越长,DML/DDL 操作花费的时间就越长,因此对象上的锁被持有的时间也就越长。这会对并发/性能产生不利影响。将 DML / DDL 语句绑定到对本地(内联网/同一网络)服务的 Web 服务调用,这让许多人感到非常紧张,但通过互联网正在将危险的、高度可变的风险引入本应相当简单的过程中。当然,如果您使用的是 Azure VM 或 Azure SQL 托管实例,那么延迟可能足够低以确保安全 "enough"。无论哪种方式,都要非常谨慎! (明确一点:使用 T-SQL 触发器执行 SQLCLR 存储过程并不能减轻这种风险;这仍然是同一个事务)
- 如果触发器失败/抛出错误,默认情况下将中止事务并回滚 DML(或 DDL)操作。这是有意的行为吗?通常未能记录事件不应该中止操作本身,对吗?所以您可能需要吞下错误(或者至少将其写入文本文件)。
您应该能够通过设置 Service Broker 来处理调用日志服务来将 DML/DLL 操作与日志片段分离。在这种情况下,您将:
- 使用 T-SQL 触发器
- 通过以下方式获取 table 名称:
SELECT tab.[name]
FROM sys.objects tab
WHERE tab.[object_id] = (
SELECT trg.[parent_object_id]
FROM sys.objects trg
WHERE trg.[object_id] = @@PROCID
);
- 收集任何其他信息来记录,可能来自
INSERTED
and/or DELETED
tables(此后您将无法访问这些信息;尽管您可能会将这两个 table 中的数据重新打包为 XML 以作为 Service Broker 消息的一部分发送——例如 SELECT * FROM inserted FOR XML RAW('ins');
)
- 为 Service Broker 排队消息,包括收集的信息
- 使用服务代理
- 将异步处理来自 DML/DLL 操作的消息
- 可以执行SQLCLR存储过程,传入T-SQL触发器收集的信息,调用日志服务(无论是内网还是外网)
请记住(因为您提到从 INSERTED
和 DELETED
tables 中获取受影响的 PK 值)如果这些 tables 中可以有多个行DML 操作影响了多行(即 从不 假设 DML 操作只有一行)。
我正在用 C# 创建一个相对简单的 SQL CLR。
此 CLR 的工作是将事件发布到 Azure 事件网格主题。
我希望能够从任意数量的触发器为任意数量的 table 调用这个特定的 CLR(实际上很少)。
鉴于下面的核心代码,如何准确检索 caused/fired 触发器的 table 的名称?我不知道从哪里开始。
我希望能够删除 SqlTrigger
属性并以某种方式访问 var table
属性.
// I Would like to leave the attribute commented out
//
//[Microsoft.SqlServer.Server.SqlTrigger(Name = "PublishToEventGridTrigger",
// Target = "NamesOfThings", Event = "FOR UPDATE, INSERT, DELETE")]
public static void PublishToEventGridTrigger()
{
// TODO - How should these settings be handled?
// Not sure if config files are accessible... To test..
// ***************************************************************
string topicHost = "https://####.eventgrid.azure.net/api/events";
string topicKey = "####";
// TODO - Get Table name for this
var table = "How Do I set This";
string eventType = $"##.{table}.{SqlContext.TriggerContext.TriggerAction.ToString()}";
// ***************************************************************
try
{
// extract data involved in trigger
// Create the Event object
EventGridEvent evt = new EventGridEvent("QWDBEvent", eventType, new
TriggerData(SqlContext.TriggerContext, SqlContext.Pipe));
// Publish the event
Event.Publish(topicHost, topicKey, evt);
}
catch (Exception ex)
{
//TODO - how do we handle these through SQL CLR?
// Going with a fire-and-forget for now
SqlContext.Pipe.Send($"Failure firing {ex.Message}");
}
SqlContext.Pipe.Send($"Trigger fired");
}
这不是本地支持的,也不是很容易实现。我实际上有一个模型可以做到这一点,但还没有时间发布它。当然不是直截了当的。目前,你可以看看这个问题的两个答案:
我会在清理并发布我的解决方案后更新此答案。
HOWEVER,虽然您说只有少数 table 会应用此功能,但请记住触发器是在DML(甚至 DDL)语句所属的事务的上下文。这有两个后果:
- 触发器花费的时间越长,DML/DDL 操作花费的时间就越长,因此对象上的锁被持有的时间也就越长。这会对并发/性能产生不利影响。将 DML / DDL 语句绑定到对本地(内联网/同一网络)服务的 Web 服务调用,这让许多人感到非常紧张,但通过互联网正在将危险的、高度可变的风险引入本应相当简单的过程中。当然,如果您使用的是 Azure VM 或 Azure SQL 托管实例,那么延迟可能足够低以确保安全 "enough"。无论哪种方式,都要非常谨慎! (明确一点:使用 T-SQL 触发器执行 SQLCLR 存储过程并不能减轻这种风险;这仍然是同一个事务)
- 如果触发器失败/抛出错误,默认情况下将中止事务并回滚 DML(或 DDL)操作。这是有意的行为吗?通常未能记录事件不应该中止操作本身,对吗?所以您可能需要吞下错误(或者至少将其写入文本文件)。
您应该能够通过设置 Service Broker 来处理调用日志服务来将 DML/DLL 操作与日志片段分离。在这种情况下,您将:
- 使用 T-SQL 触发器
- 通过以下方式获取 table 名称:
SELECT tab.[name] FROM sys.objects tab WHERE tab.[object_id] = ( SELECT trg.[parent_object_id] FROM sys.objects trg WHERE trg.[object_id] = @@PROCID );
- 收集任何其他信息来记录,可能来自
INSERTED
and/orDELETED
tables(此后您将无法访问这些信息;尽管您可能会将这两个 table 中的数据重新打包为 XML 以作为 Service Broker 消息的一部分发送——例如SELECT * FROM inserted FOR XML RAW('ins');
) - 为 Service Broker 排队消息,包括收集的信息
- 通过以下方式获取 table 名称:
- 使用服务代理
- 将异步处理来自 DML/DLL 操作的消息
- 可以执行SQLCLR存储过程,传入T-SQL触发器收集的信息,调用日志服务(无论是内网还是外网)
请记住(因为您提到从 INSERTED
和 DELETED
tables 中获取受影响的 PK 值)如果这些 tables 中可以有多个行DML 操作影响了多行(即 从不 假设 DML 操作只有一行)。