Как передать аргументы внешнему (SQLCLR) триггеру SQL Server
Я создал триггер, который вызывает сборку, подобную этой:
CREATE TRIGGER Testrigger ON STATION
FOR INSERT
AS EXTERNAL NAME assemblytest.[WriteTimeInfile.Program].Testrigger
Код.NET в этой сборке, который делает что-то вроде этого:
namespace WriteTimeInfile
{
public class Program
{
[SqlTrigger(Name = @"Testrigger", Target = "[dbo].[STATION]", Event = "FOR INSERT, UPDATE, DELETE")]
public static void Testrigger()
{
File.AppendAllText(@"C:\Users\Vivien\date.txt",
DateTime.Now.ToString() + Environment.NewLine);
}
}
}
Я хотел бы иметь возможность передать в качестве аргумента созданную строку или обновленную строку примерно так:
CREATE TRIGGER Testrigger ON STATION
AFTER INSERT
AS
EXTERNAL NAME assemblytest.[WriteTimeInfile.Program].Testrigger (STATION.ID)
Я нашел 7-летнюю тему о Stackru, в которой говорится, что нет способа передать аргумент в сборку CLR.
Я спрашиваю, возможно ли это сейчас в последних версиях SQL Server.
Знаете ли вы, есть ли способ, и если да, как это сделать, пожалуйста?
2 ответа
Нет, вы не можете напрямую передавать аргументы в триггеры SQLCLR. Однако вы можете передавать значения косвенно несколькими способами (так же, как с обычными триггерами T-SQL):
- Локальная временная таблица
- SET CONTEXT_INFO / CONTEXT_INFO
- В SQL Server 2016 или новее: sp_set_session_context / SESSION_CONTEXT
Во всех случаях вы получите значения, выполнив SqlCommand
с выходом SqlParameter
вытащить значение в код.NET. (Пожалуйста, смотрите примечание в конце относительно использования).
НО, если вы просто хотите значения inserted
и / или deleted
таблицы, это не были бы аргументы или параметры. Просто SELECT
те, кто использует SqlCommand
с помощью Context Connection = true
для строки подключения и SqlDataReader
, Пример этого можно увидеть на странице MSDN для триггеров CLR, в разделе Пример триггера CLR.
Примечание относительно передачи значений в триггеры, которые не являются частью операции DML:
Хотя это не очень распространено, безусловно, существуют допустимые варианты использования для передачи фрагмента информации из основного контекста одному или нескольким триггерам в цепочке событий. Два наиболее распространенных случая, с которыми я сталкивался: 1) передача логина на основе приложения или идентификатора пользователя (не являющегося частью SQL Server) триггеру аудита для удаления строк (поскольку эта информация не может быть добавлена в столбец ModifiedBy в DELETE
операция), и 2) временно отключить триггер на основе условия. И да, это возможно, и это работает. Пожалуйста, смотрите следующие мои ответы на DBA.StackExchange:
Псевдостолы INSERTED и DELETED всегда были доступны для прямого запроса внутри триггера SQLCLR, как показано в этой более чем 9-летней версии документации. Вы всегда можете запросить их, например:
using (SqlConnection conn = new SqlConnection("context connection=true"))
{
conn.Open();
SqlCommand sqlComm = new SqlCommand();
SqlPipe sqlP = SqlContext.Pipe;
sqlComm.Connection = conn;
sqlComm.CommandText = "SELECT UserName from INSERTED";
userName.Value = sqlComm.ExecuteScalar().ToString();
if (IsEMailAddress(userName.Value.ToString()))
{
sqlComm.Parameters.Add(userName);
sqlComm.CommandText = "INSERT UsersAudit(UserName) VALUES(@username)";
sqlP.Send(sqlComm.CommandText);
sqlP.ExecuteAndSend(sqlComm);
}
}
Последние образцы одинаковы.
Вам не нужно передавать их как параметры, так же как вам не нужно передавать их как параметры обычным триггерам. Таблицы всегда доступны для запросов.
Я подозреваю, что эти таблицы не отображаются как, например, коллекции на объекте контекста, потому что это потребовало бы копирования их из буферов SQL Server (тратить впустую ЦП и память). Другая причина заключается в том, что к коллекции нельзя получить эффективный запрос. Вам придется либо использовать LINQ (используя еще больше ЦП), либо просто перебирать все содержимое (память и ЦП). Потеря памяти была бы большей проблемой, так как эта память могла бы использоваться для буферизации большего количества данных и индексов, тем самым ускоряя доступ.
Я подозреваю, что вопрос, на который вы ссылались, хотел задать то же самое, но ОП предположил, что псевдотаблицы должны передаваться в качестве параметров. Поэтому он спросил о параметрах, а не о фактической проблеме.