访问 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)语句所属的事务的上下文。这有两个后果:

  1. 触发器花费的时间越长,DML/DDL 操作花费的时间就越长,因此对象上的锁被持有的时间也就越长。这会对并发/性能产生不利影响。将 DML / DDL 语句绑定到对本地(内联网/同一网络)服务的 Web 服务调用,这让许多人感到非常紧张,但通过互联网正在将危险的、高度可变的风险引入本应相当简单的过程中。当然,如果您使用的是 Azure VM 或 Azure SQL 托管实例,那么延迟可能足够低以确保安全 "enough"。无论哪种方式,都要非常谨慎! (明确一点:使用 T-SQL 触发器执行 SQLCLR 存储过程并不能减轻这种风险;这仍然是同一个事务
  2. 如果触发器失败/抛出错误,默认情况下将中止事务并回滚 DML(或 DDL)操作。这是有意的行为吗?通常未能记录事件不应该中止操作本身,对吗?所以您可能需要吞下错误(或者至少将其写入文本文件)。

您应该能够通过设置 Service Broker 来处理调用日志服务来将 DML/DLL 操作与日志片段分离。在这种情况下,您将:

  1. 使用 T-SQL 触发器
    1. 通过以下方式获取 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
                               );
      
    2. 收集任何其他信息来记录,可能来自 INSERTED and/or DELETED tables(此后您将无法访问这些信息;尽管您可能会将这两个 table 中的数据重新打包为 XML 以作为 Service Broker 消息的一部分发送——例如 SELECT * FROM inserted FOR XML RAW('ins'); )
    3. 为 Service Broker 排队消息,包括收集的信息
  2. 使用服务代理
    1. 将异步处理来自 DML/DLL 操作的消息
    2. 可以执行SQLCLR存储过程,传入T-SQL触发器收集的信息,调用日志服务(无论是内网还是外网)

请记住(因为您提到从 INSERTEDDELETED tables 中获取受影响的 PK 值)如果这些 tables 中可以有多个行DML 操作影响了多行(即 从不 假设 DML 操作只有一行)。