Доступ к имени триггерной/родительской таблицы внутри триггера SQLCLR

Я создаю относительно простую 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");
}

person Darren Wainwright    schedule 04.02.2020    source источник
comment
Это изначально не поддерживается, и это нелегко выполнить. На самом деле у меня есть макет, который это делает, но я не успел его опубликовать. Конечно не прямолинейно. Кроме того, этот вопрос является дубликатом как минимум 1 или 2 других, но мне нужно их найти, так как я их давно не видел.   -  person Solomon Rutzky    schedule 04.02.2020
comment
@SolomonRutzky- большое спасибо. Я видел здесь пару других вопросов, но ничего, что определяло бы какой-либо фактический ответ. Любые другие ссылки были бы потрясающими. Если это сложно сделать, я передумаю и выберу CLR для каждого триггера, где это необходимо. Просто надеялся иметь общий диспетчер событий.   -  person Darren Wainwright    schedule 04.02.2020
comment
Вероятно, вам следует просто сделать это хранимой процедурой CLR и вызывать ее из триггеров TSQL, передавая любые необходимые аргументы.   -  person David Browne - Microsoft    schedule 04.02.2020
comment
@DavidBrowne-Microsoft - о. Это кажется разумной идеей. Спасибо. Я попробую.   -  person Darren Wainwright    schedule 04.02.2020
comment
@DarrenWainwright Вам нужен для этого доступ к таблицам INSERTED и DELETED? Или просто знать, что событие произошло и за каким столом оно было?   -  person Solomon Rutzky    schedule 04.02.2020
comment
@SolomonRutzky - в настоящее время я обращаюсь к ним, чтобы получить ПК любого объекта, который был вставлен/обновлен или удален.   -  person Darren Wainwright    schedule 04.02.2020


Ответы (1)


Это изначально не поддерживается, и это не очень легко сделать. На самом деле у меня есть макет, который это делает, но я не успел его опубликовать. Конечно не прямолинейно. На данный момент вы можете взглянуть на оба ответа на этот вопрос:

Триггер SQL CLR — получить исходную таблицу

И я обновлю этот ответ, когда мое решение будет очищено и опубликовано.

ОДНАКО, хотя вы говорите, что это будет применено только к нескольким таблицам, имейте в виду, что триггеры выполняются в контексте транзакции, которую выполняет оператор DML (или даже DDL). является частью. Это имеет два следствия:

  1. чем дольше работает триггер, тем дольше выполняется операция DML/DDL, следовательно, тем дольше удерживаются блокировки объекта(ов). Это может отрицательно сказаться на параллелизме/производительности. Многие нервничают, связывая операторы DML/DDL с вызовами веб-сервисов к локальным (внутрисетевым/в той же сети) сервисам, но через Интернет в процесс, который должен быть довольно простым, вносится опасный, сильно варьирующийся риск. Конечно, если вы используете виртуальную машину Azure или управляемый экземпляр Azure SQL, то, возможно, задержка будет достаточно низкой, чтобы это было «достаточно безопасным». В любом случае, будьте очень осторожны! (Для ясности: этот риск не снижается при использовании триггера T-SQL для выполнения хранимой процедуры SQLCLR; это все та же транзакция)
  2. если триггер не работает / выдает ошибку, по умолчанию это прерывает транзакцию и откатывает операцию DML (или DDL). Это предполагаемое поведение? Обычно сбой в регистрации события не должен прерывать саму операцию, верно? Поэтому вам, вероятно, придется проглотить ошибку (или, по крайней мере, записать ее в текстовый файл).

Вы должны иметь возможность отделить операцию DML/DLL от части ведения журнала, настроив Service Broker для обработки вызова службы ведения журнала. В этом случае вы бы:

  1. Use a T-SQL trigger
    1. get the table name via:
      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. Use Service Broker
    1. will handle messages asynchronously from DML / DLL operations
    2. может выполнять хранимую процедуру SQLCLR, передавая информацию, собранную в триггере T-SQL, для вызова службы ведения журнала (будь то внутренняя или внешняя сеть)

Имейте в виду (поскольку вы упомянули получение затронутого значения PK из таблиц INSERTED и DELETED), что в этих таблицах может быть несколько строк, если операция DML затрагивала несколько строк (т. е. никогда не предполагала одну строку для операций DML). ).

person Solomon Rutzky    schedule 04.02.2020
comment
Спасибо за хорошо продуманное и описанное решение. Сборка CLR, которую я собираю, предназначена для доставки базового сообщения в сетку событий Azure — аналогично брокеру здесь. Если я создам стандартный триггер TSQL, который вызовет SQL CLR Sproc, и этот вызов sproc потерпит неудачу, произойдет ли сбой всей транзакции? Аналогично, можно ли вызвать sproc CLR, и триггеру не нужно ждать какого-либо ответа? (задержка мышления при вызове сетки событий) - person Darren Wainwright; 04.02.2020
comment
@DarrenWainwright Еще раз привет. Я уже обращался к этой первой части в выделенном курсивом последнем предложении первого последствия: если вы не предпримете каких-то специальных действий, чтобы предотвратить это, ДА, любой сбой в триггере или что-либо, что он выполняет, отменит/откатит всю операцию/транзакцию DML. Триггеры неявно имеют SET XACT_ABORT ON, так что любая ошибка является немедленным исключением для всего процесса. Вы можете установить для него значение OFF, но тогда вам лучше не иметь никакой другой логики, которая должна отменить операцию DML в случае ошибки. Поэтому я предложил проглотить ошибку в .NET, сохранив в текстовый файл. - person Solomon Rutzky; 04.02.2020
comment
@DarrenWainwright Для части 2: вы можете вызвать HttpWebRequest async, но для этого потребуется, чтобы сборка была установлена ​​​​на UNSAFE, а не только на EXTERNAL_ACCESS. Я не пробовал это. Не уверен, имеет ли значение, что вызывающий процесс (т. е. метод, который является хранимой процедурой SQLCLR) завершится к моменту возврата ответа (именно так это работает для статического метода, верно?). Не уверен, что это приведет к осиротевшим потокам, у которых нет пути к GC или что-то в этом роде. Но, возможно, стоит протестировать. - person Solomon Rutzky; 04.02.2020
comment
Спасибо еще раз. Ясно, что немного из этого вылетело из моей головы :) Я собираюсь дать этому подходу вихрь. Я также рад отметить это как ответ. - person Darren Wainwright; 04.02.2020
comment
@DarrenWainwright Удачи, и дайте мне знать, как все идет. - person Solomon Rutzky; 05.02.2020
comment
обязательно сделаю. Еще раз спасибо. Вы фантастическое представление того, как это сообщество делает потрясающие :) - person Darren Wainwright; 05.02.2020
comment
@DarrenWainwright Не за что. И спасибо, это очень мило с вашей стороны ????. - person Solomon Rutzky; 07.02.2020