【问题标题】:Accessing the triggered / parent table name inside a SQLCLR trigger访问 SQLCLR 触发器内的触发/父表名
【发布时间】:2020-05-20 10:49:55
【问题描述】:

我正在用 C# 创建一个相对简单的 SQL CLR。

此 CLR 的工作是将事件发布到 Azure 事件网格主题。

我希望能够从任意数量的表的任意数量的触发器中调用这个特定的 CLR(实际上会很少)。

鉴于下面的核心代码,如何准确检索导致/触发触发器的表的名称?我不知道从哪里开始。

我希望能够删除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");
}

【问题讨论】:

  • 这不是本机支持的,也不容易实现。我实际上有一个模型可以做到,但还没有时间发布它。当然不是直截了当。此外,这个问题至少与其他 1 或 2 个问题重复,但我需要搜索它们,因为我已经有一段时间没有看到它们了。
  • @SolomonRutzky- 非常感谢。我确实在这里看到了其他几个问题,但没有定义任何实际答案。任何其他链接都会很棒。如果完成起来很痛苦,那么我会重新考虑并在需要时为每个触发器使用 CLR。只是希望有一个通用的事件调度程序。
  • 您可能应该将其设为 CLR 存储过程,然后从 TSQL 触发器中调用它并传入任何必需的参数。
  • @DavidBrowne-Microsoft - 哦。这似乎是一个合理的想法。谢谢。我会试一试。
  • @DarrenWainwright 为此您需要访问INSERTEDDELETED 表吗?或者只是想知道事件发生了以及它在哪张桌子上?

标签: c# sql-server database-trigger sqlclr azure-eventgrid


【解决方案1】:

这不是本机支持的,也不是很容易实现。我实际上有一个模型可以做到,但还没有时间发布它。当然不是直截了当。目前,您可以查看此问题的两个答案:

SQL CLR Trigger - get source table

当我清理并发布我的解决方案时,我会更新这个答案。

然而,虽然您说只有少数表会应用此功能,但请记住,触发器是在 DML(甚至DDL) 语句是其中的一部分。这有两个后果:

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

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

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

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

【讨论】:

  • 感谢您提供深思熟虑和描述的解决方案。我正在组装的 CLR 程序集用于向 Azure 的事件网格传递基本消息 - 类似于此处的代理。如果我构建了一个调用 SQL CLR Sproc 的标准 TSQL 触发器并且该 sproc 调用失败,整个事务会失败吗?同样,可以调用 CLR sproc 并且触发器不必等待任何类型的响应吗? (在调用事件网格时考虑延迟)
  • @DarrenWainwright 再次嗨。我已经在第一个结果的斜体最后一句中解决了第一部分:除非你做一些特殊的事情来防止它,是的,触发器中的任何失败或它执行的任何事情都将取消/回滚整个 DML 操作/事务。触发器隐含地具有SET XACT_ABORT ON,因此任何错误都是整个过程的直接异常。您可以将其设置为OFF,但如果出现错误,您最好不要有任何其他应该取消DML 操作的逻辑。因此,为什么我建议在 .NET 中吞下错误,将其保存到文本文件中。
  • @DarrenWainwright 对于第 2 部分:您可能可以调用 HttpWebRequest async,但这需要将程序集设置为 UNSAFE 而不仅仅是 EXTERNAL_ACCESS。我没试过这个。不确定调用进程(即 SQLCLR 存储过程的方法)是否会在响应返回时终止(这就是静态方法的工作方式,对吗?)。不确定这是否会孤立无法进行 GC 或什么的线程。但它可能值得测试。
  • 再次感谢。很明显,其中的一部分让我有点不知所措:) 我要试一试这种方法。我也很高兴将此标记为已回答。
  • 我一定会的。再次感谢你。你是这个社区如何出色的代表:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 2016-08-09
相关资源
最近更新 更多